| @@ -0,0 +1,13 @@ | |||
| # Initialize CMake ################################################################################ | |||
| CMake_Minimum_Required ( VERSION 3.5.1 FATAL_ERROR ) | |||
| If ( NOT CMAKE_BUILD_TYPE ) | |||
| Set ( CMAKE_BUILD_TYPE "Release" CACHE STRING "Choose the type of build!" FORCE ) | |||
| EndIf ( NOT CMAKE_BUILD_TYPE ) | |||
| Set ( CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/../../../inc/cmake/") | |||
| # Projects ######################################################################################## | |||
| Add_SubDirectory ( ${CMAKE_CURRENT_SOURCE_DIR}/src ) | |||
| Add_SubDirectory ( ${CMAKE_CURRENT_SOURCE_DIR}/test ) | |||
| @@ -0,0 +1,19 @@ | |||
| #pragma once | |||
| #include <cppamqp/channel.h> | |||
| #include <cppamqp/connection.h> | |||
| #include <cppamqp/consume_result.h> | |||
| #include <cppamqp/enums.h> | |||
| #include <cppamqp/exception.h> | |||
| #include <cppamqp/helper.h> | |||
| #include <cppamqp/message.h> | |||
| #include <cppamqp/publish_options.h> | |||
| #include <cppamqp/types.h> | |||
| #include <cppamqp/channel.inl> | |||
| #include <cppamqp/connection.inl> | |||
| #include <cppamqp/consume_result.inl> | |||
| #include <cppamqp/exception.inl> | |||
| #include <cppamqp/helper.inl> | |||
| #include <cppamqp/message.inl> | |||
| #include <cppamqp/publish_options.inl> | |||
| @@ -0,0 +1,50 @@ | |||
| #pragma once | |||
| #include <memory> | |||
| #include <cppamqp/enums.h> | |||
| #include <cppamqp/types.h> | |||
| #include <cppamqp/config.h> | |||
| #include <cppamqp/connection.fwd.h> | |||
| #include <cppamqp/publish_options.h> | |||
| namespace cppamqp | |||
| { | |||
| struct channel | |||
| { | |||
| private: | |||
| struct internal | |||
| { | |||
| const connection& connection; | |||
| channel_number handle; | |||
| internal(const ::cppamqp::connection& p_connection, channel_number p_handle); | |||
| ~internal(); | |||
| }; | |||
| private: | |||
| friend struct connection; | |||
| std::shared_ptr<internal> _internal; | |||
| inline channel(const connection& p_connection, channel_number p_handle); | |||
| public: | |||
| inline channel(); | |||
| inline channel(channel&& other); | |||
| inline ~channel(); | |||
| inline operator bool () const; | |||
| inline void operator = (channel&& other); | |||
| inline channel_number handle () const; | |||
| inline const connection& connection () const; | |||
| queue_declaration declare_queue (const std::string& name, const queue_flags& flags); | |||
| void bind_queue (const std::string& queue, const std::string& exchange, const std::string& routing_key); | |||
| void publish (const std::string& exchange, const std::string& routing_key, const publish_flags& flags, const std::string& message, const publish_options* options = nullptr); | |||
| std::string consume (const std::string& queue, const std::string& consumer_tag, const consume_flags& flags); | |||
| void close (int status = AMQP_REPLY_SUCCESS); | |||
| }; | |||
| } | |||
| @@ -0,0 +1,48 @@ | |||
| #pragma once | |||
| #include <memory> | |||
| #include <cppamqp/enums.h> | |||
| #include <cppamqp/types.h> | |||
| #include <cppamqp/config.h> | |||
| #include <cppamqp/channel.h> | |||
| #include <cppamqp/exception.inl> | |||
| #include <cppamqp/connection.fwd.h> | |||
| namespace cppamqp | |||
| { | |||
| channel::channel(const cppamqp::connection& p_connection, channel_number p_handle) | |||
| : _internal(new internal(p_connection, p_handle)) | |||
| { } | |||
| channel::channel() | |||
| { } | |||
| channel::channel(channel&& other) | |||
| : _internal(std::move(other._internal)) | |||
| { } | |||
| channel::~channel() | |||
| { } | |||
| channel::operator bool() const | |||
| { return static_cast<bool>(_internal); } | |||
| void channel::operator =(channel&& other) | |||
| { _internal = std::move(other._internal); } | |||
| channel_number channel::handle() const | |||
| { | |||
| if (!_internal) | |||
| throw exception("channel is closed!"); | |||
| return _internal->handle; | |||
| } | |||
| const connection& channel::connection() const | |||
| { | |||
| if (!_internal) | |||
| throw exception("channel is closed!"); | |||
| return _internal->connection; | |||
| } | |||
| } | |||
| @@ -0,0 +1,4 @@ | |||
| #pragma once | |||
| #include <amqp.h> | |||
| #include <amqp_tcp_socket.h> | |||
| @@ -0,0 +1,8 @@ | |||
| #pragma once | |||
| namespace cppamqp | |||
| { | |||
| struct connection; | |||
| } | |||
| @@ -0,0 +1,62 @@ | |||
| #pragma once | |||
| #include <chrono> | |||
| #include <string> | |||
| #include <memory> | |||
| #include <cppamqp/types.h> | |||
| #include <cppamqp/config.h> | |||
| #include <cppamqp/channel.h> | |||
| #include <cppamqp/consume_result.h> | |||
| namespace cppamqp | |||
| { | |||
| struct connection | |||
| { | |||
| private: | |||
| static inline const std::string& default_vhost (); | |||
| static inline const std::string& default_username (); | |||
| static inline const std::string& default_password (); | |||
| static inline const std::chrono::milliseconds& default_consume_timeout (); | |||
| private: | |||
| struct internal | |||
| { | |||
| amqp_connection_state_t connection; | |||
| int reply_code; | |||
| bool auto_close { false }; | |||
| internal(); | |||
| ~internal(); | |||
| }; | |||
| private: | |||
| std::unique_ptr<internal> _internal; | |||
| public: | |||
| inline amqp_connection_state_t& handle (); | |||
| inline const amqp_connection_state_t& handle () const; | |||
| void tcp_connect( | |||
| const std::string& hostname, | |||
| uint port); | |||
| void login_plain( | |||
| const std::string& p_username = default_username(), | |||
| const std::string& p_password = default_password(), | |||
| const std::string& p_vhost = default_vhost(), | |||
| int p_max_channels = AMQP_DEFAULT_MAX_CHANNELS, | |||
| int p_max_frame_size = AMQP_DEFAULT_FRAME_SIZE); | |||
| void login_external( | |||
| const std::string& p_identify, | |||
| const std::string& p_vhost = default_vhost(), | |||
| int p_max_channels = AMQP_DEFAULT_MAX_CHANNELS, | |||
| int p_max_frame_size = AMQP_DEFAULT_FRAME_SIZE); | |||
| channel open_channel (channel_number p_channel); | |||
| consume_result consume_message(const std::chrono::milliseconds& p_timeout = default_consume_timeout()); | |||
| void close (int p_status = AMQP_REPLY_SUCCESS, bool p_force = false); | |||
| }; | |||
| } | |||
| @@ -0,0 +1,52 @@ | |||
| #pragma once | |||
| #include <chrono> | |||
| #include <string> | |||
| #include <memory> | |||
| #include <cppamqp/types.h> | |||
| #include <cppamqp/config.h> | |||
| #include <cppamqp/connection.h> | |||
| #include <cppamqp/channel.inl> | |||
| namespace cppamqp | |||
| { | |||
| const std::string& connection::default_vhost() | |||
| { | |||
| static const std::string value("/"); | |||
| return value; | |||
| } | |||
| const std::string& connection::default_username() | |||
| { | |||
| static const std::string value("guest"); | |||
| return value; | |||
| } | |||
| const std::string& connection::default_password() | |||
| { | |||
| static const std::string value("guest"); | |||
| return value; | |||
| } | |||
| const std::chrono::milliseconds& connection::default_consume_timeout() | |||
| { | |||
| static const std::chrono::milliseconds value(-1); | |||
| return value; | |||
| } | |||
| amqp_connection_state_t& connection::handle() | |||
| { | |||
| if (!_internal) | |||
| throw exception("connection is closed"); | |||
| return _internal->connection; | |||
| } | |||
| const amqp_connection_state_t& connection::handle() const | |||
| { | |||
| if (!_internal) | |||
| throw exception("connection is closed"); | |||
| return _internal->connection; | |||
| } | |||
| } | |||
| @@ -0,0 +1,38 @@ | |||
| #pragma once | |||
| #include <string> | |||
| #include <cppamqp/enums.h> | |||
| #include <cppamqp/types.h> | |||
| #include <cppamqp/config.h> | |||
| #include <cppamqp/message.h> | |||
| #include <cppamqp/connection.fwd.h> | |||
| namespace cppamqp | |||
| { | |||
| struct consume_result | |||
| { | |||
| private: | |||
| const connection& _connection; | |||
| public: | |||
| consume_result_type type; | |||
| message message; | |||
| channel_number channel; | |||
| std::string consumer_tag; | |||
| uint64_t delivery_tag; | |||
| bool redelivered; | |||
| std::string exchange; | |||
| std::string routing_key; | |||
| inline consume_result(const connection& p_connection, consume_result_type p_type); | |||
| inline consume_result(const connection& p_connection, consume_result_type p_type, const amqp_message_t& p_message); | |||
| inline consume_result(const connection& p_connection, const amqp_envelope_t& p_envelope); | |||
| inline consume_result(consume_result&& other) = default; | |||
| inline consume_result(const consume_result& other) = default; | |||
| inline void ack(); | |||
| inline void nack(bool requeue); | |||
| }; | |||
| } | |||
| @@ -0,0 +1,50 @@ | |||
| #pragma once | |||
| #include <cppamqp/enums.h> | |||
| #include <cppamqp/helper.h> | |||
| #include <cppamqp/connection.h> | |||
| #include <cppamqp/consume_result.h> | |||
| namespace cppamqp | |||
| { | |||
| consume_result::consume_result(const connection& p_connection, consume_result_type p_type) | |||
| : _connection (p_connection) | |||
| , type (p_type) | |||
| { } | |||
| consume_result::consume_result(const connection& p_connection, consume_result_type p_type, const amqp_message_t& p_message) | |||
| : _connection (p_connection) | |||
| , type (p_type) | |||
| , message (p_message) | |||
| { } | |||
| consume_result::consume_result(const connection& p_connection, const amqp_envelope_t& p_envelope) | |||
| : _connection (p_connection) | |||
| , type (consume_result_type::success) | |||
| , message (p_envelope.message) | |||
| , channel (p_envelope.channel) | |||
| , consumer_tag (__impl::from_bytes(p_envelope.consumer_tag)) | |||
| , delivery_tag (p_envelope.delivery_tag) | |||
| , redelivered (p_envelope.redelivered) | |||
| , exchange (__impl::from_bytes(p_envelope.exchange)) | |||
| , routing_key (__impl::from_bytes(p_envelope.routing_key)) | |||
| { } | |||
| void consume_result::ack() | |||
| { | |||
| __impl::check_and_raise( | |||
| amqp_basic_ack(_connection.handle(), channel, delivery_tag, false), | |||
| "error while ack message", | |||
| false); | |||
| } | |||
| void consume_result::nack(bool requeue) | |||
| { | |||
| __impl::check_and_raise( | |||
| amqp_basic_nack(_connection.handle(), channel, delivery_tag, false, requeue), | |||
| "error while nack message", | |||
| false); | |||
| } | |||
| } | |||
| @@ -0,0 +1,48 @@ | |||
| #pragma once | |||
| #include <cppamqp/config.h> | |||
| #include <cpputils/misc/flags.h> | |||
| namespace cppamqp | |||
| { | |||
| enum class delivery_mode | |||
| { | |||
| non_persistent = AMQP_DELIVERY_NONPERSISTENT, | |||
| persistent = AMQP_DELIVERY_PERSISTENT, | |||
| }; | |||
| enum class consume_result_type | |||
| { | |||
| success, | |||
| timeout, | |||
| acknowledge, | |||
| connection_closed_by_peer, | |||
| could_not_deliver_return_to_sender, | |||
| }; | |||
| enum class queue_flag | |||
| { | |||
| passive, | |||
| durable, | |||
| exclusive, | |||
| auto_delete, | |||
| }; | |||
| using queue_flags = utl::shifted_flags<queue_flag>; | |||
| enum class publish_flag | |||
| { | |||
| mandatory, | |||
| immediate, | |||
| }; | |||
| using publish_flags = utl::shifted_flags<publish_flag>; | |||
| enum class consume_flag | |||
| { | |||
| no_local, | |||
| no_ack, | |||
| exclusive, | |||
| }; | |||
| using consume_flags = utl::shifted_flags<consume_flag>; | |||
| } | |||
| @@ -0,0 +1,21 @@ | |||
| #pragma once | |||
| #include <string> | |||
| #include <cppamqp/enums.h> | |||
| #include <cppamqp/config.h> | |||
| #include <cpputils/misc/exception.h> | |||
| namespace cppamqp | |||
| { | |||
| struct exception | |||
| : public utl::exception | |||
| { | |||
| amqp_status_enum status { AMQP_STATUS_OK }; | |||
| inline exception(const std::string& p_message); | |||
| inline exception(amqp_status_enum p_status); | |||
| inline exception(const std::string& p_message, amqp_status_enum p_status); | |||
| }; | |||
| } | |||
| @@ -0,0 +1,22 @@ | |||
| #pragma once | |||
| #include <cppamqp/exception.h> | |||
| namespace cppamqp | |||
| { | |||
| exception::exception(const std::string& p_message) : | |||
| utl::exception (p_message) | |||
| { } | |||
| exception::exception(amqp_status_enum p_status) : | |||
| utl::exception (amqp_error_string2(p_status)), | |||
| status (p_status) | |||
| { } | |||
| exception::exception(const std::string& p_message, amqp_status_enum p_status) : | |||
| utl::exception (p_message), | |||
| status (p_status) | |||
| { } | |||
| } | |||
| @@ -0,0 +1,17 @@ | |||
| #pragma once | |||
| #include <string> | |||
| #include <cppamqp/config.h> | |||
| namespace cppamqp | |||
| { | |||
| namespace __impl | |||
| { | |||
| void check_and_raise (int returnCode, std::string msg, bool logOnly); | |||
| void check_and_raise (const amqp_rpc_reply_t& reply, std::string msg, bool logOnly); | |||
| inline amqp_bytes_t make_bytes (const std::string& s); | |||
| inline std::string from_bytes (const amqp_bytes_t& data); | |||
| } | |||
| } | |||
| @@ -0,0 +1,22 @@ | |||
| #pragma once | |||
| #include <cppamqp/helper.h> | |||
| namespace cppamqp | |||
| { | |||
| namespace __impl | |||
| { | |||
| amqp_bytes_t make_bytes(const std::string& s) | |||
| { | |||
| return amqp_bytes_t { | |||
| s.size(), | |||
| const_cast<char*>(s.c_str()), | |||
| }; | |||
| } | |||
| std::string from_bytes(const amqp_bytes_t& data) | |||
| { return std::string(static_cast<const char*>(data.bytes), data.len); } | |||
| } | |||
| } | |||
| @@ -0,0 +1,20 @@ | |||
| #pragma once | |||
| #include <cppamqp/config.h> | |||
| #include <cppamqp/publish_options.h> | |||
| namespace cppamqp | |||
| { | |||
| struct message | |||
| { | |||
| publish_options options; | |||
| std::string body; | |||
| inline message(); | |||
| inline message(const amqp_message_t& msg); | |||
| inline message(message&& other) = default; | |||
| inline message(const message& other) = default; | |||
| }; | |||
| } | |||
| @@ -0,0 +1,17 @@ | |||
| #pragma once | |||
| #include <cppamqp/helper.h> | |||
| #include <cppamqp/message.h> | |||
| namespace cppamqp | |||
| { | |||
| message::message() | |||
| { } | |||
| message::message(const amqp_message_t& msg) | |||
| : options (msg.properties) | |||
| , body (__impl::from_bytes(msg.body)) | |||
| { } | |||
| } | |||
| @@ -0,0 +1,39 @@ | |||
| #pragma once | |||
| #include <cppamqp/enums.h> | |||
| #include <cppamqp/config.h> | |||
| #include <cpputils/container/nullable.h> | |||
| namespace cppamqp | |||
| { | |||
| struct publish_options | |||
| { | |||
| private: | |||
| mutable amqp_basic_properties_t _properties; | |||
| public: | |||
| utl::nullable<std::string> content_type; | |||
| utl::nullable<std::string> content_encoding; | |||
| utl::nullable<delivery_mode> delivery_mode; | |||
| utl::nullable<uint8_t> priority; | |||
| utl::nullable<std::string> correlation_id; | |||
| utl::nullable<std::string> reply_to; | |||
| utl::nullable<std::string> expiration; | |||
| utl::nullable<std::string> message_id; | |||
| utl::nullable<uint64_t> timestamp; | |||
| utl::nullable<std::string> type; | |||
| utl::nullable<std::string> user_id; | |||
| utl::nullable<std::string> app_id; | |||
| utl::nullable<std::string> cluster_id; | |||
| inline publish_options(); | |||
| inline publish_options(const amqp_basic_properties_t& prop); | |||
| inline publish_options(publish_options&& other) = default; | |||
| inline publish_options(const publish_options& other) = default; | |||
| const amqp_basic_properties_t* properties () const; | |||
| void reset (); | |||
| }; | |||
| } | |||
| @@ -0,0 +1,29 @@ | |||
| #pragma once | |||
| #include <cppamqp/helper.inl> | |||
| #include <cppamqp/publish_options.h> | |||
| namespace cppamqp | |||
| { | |||
| publish_options::publish_options() | |||
| { } | |||
| publish_options::publish_options(const amqp_basic_properties_t& prop) | |||
| { | |||
| if (prop._flags & AMQP_BASIC_CONTENT_TYPE_FLAG) content_type = __impl::from_bytes (prop.content_type); | |||
| if (prop._flags & AMQP_BASIC_CONTENT_ENCODING_FLAG) content_encoding = __impl::from_bytes (prop.content_encoding); | |||
| if (prop._flags & AMQP_BASIC_DELIVERY_MODE_FLAG) delivery_mode = static_cast<enum delivery_mode>(prop.delivery_mode); | |||
| if (prop._flags & AMQP_BASIC_PRIORITY_FLAG) priority = prop.priority; | |||
| if (prop._flags & AMQP_BASIC_CORRELATION_ID_FLAG) correlation_id = __impl::from_bytes (prop.correlation_id); | |||
| if (prop._flags & AMQP_BASIC_REPLY_TO_FLAG) reply_to = __impl::from_bytes (prop.reply_to); | |||
| if (prop._flags & AMQP_BASIC_EXPIRATION_FLAG) expiration = __impl::from_bytes (prop.expiration); | |||
| if (prop._flags & AMQP_BASIC_MESSAGE_ID_FLAG) message_id = __impl::from_bytes (prop.message_id); | |||
| if (prop._flags & AMQP_BASIC_TIMESTAMP_FLAG) timestamp = prop.timestamp; | |||
| if (prop._flags & AMQP_BASIC_TYPE_FLAG) type = __impl::from_bytes (prop.type); | |||
| if (prop._flags & AMQP_BASIC_USER_ID_FLAG) user_id = __impl::from_bytes (prop.user_id); | |||
| if (prop._flags & AMQP_BASIC_APP_ID_FLAG) app_id = __impl::from_bytes (prop.app_id); | |||
| if (prop._flags & AMQP_BASIC_CLUSTER_ID_FLAG) cluster_id = __impl::from_bytes (prop.cluster_id); | |||
| } | |||
| } | |||
| @@ -0,0 +1,17 @@ | |||
| #pragma once | |||
| #include <cppamqp/config.h> | |||
| namespace cppamqp | |||
| { | |||
| struct queue_declaration | |||
| { | |||
| std::string name; | |||
| uint message_count; | |||
| uint consumer_count; | |||
| }; | |||
| using channel_number = amqp_channel_t; | |||
| } | |||
| @@ -0,0 +1,36 @@ | |||
| # Initialize ###################################################################################### | |||
| Include ( cotire OPTIONAL ) | |||
| Include ( pedantic OPTIONAL ) | |||
| Include ( strip_symbols OPTIONAL ) | |||
| Option ( BUILD_SHARED_CPPAMQP "Build cppamqp shared library" OFF ) | |||
| Set ( BUILD_SHARED_LIBS ${BUILD_SHARED_CPPAMQP} ) | |||
| Set ( CMAKE_CXX_STANDARD 17 ) | |||
| Set ( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${PEDANTIC_C_FLAGS}" ) | |||
| Set ( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${PEDANTIC_CXX_FLAGS}" ) | |||
| # Dependencies #################################################################################### | |||
| Find_Package ( cpputils REQUIRED ) | |||
| # Project: cppamqp ############################################################################### | |||
| Project ( cppamqp VERSION 1.0.0.0 LANGUAGES CXX ) | |||
| File ( GLOB_RECURSE SOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp ) | |||
| Add_Library ( cppamqp ${SOURCE_FILES} ) | |||
| Target_Include_Directories ( | |||
| cppamqp | |||
| PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/../include | |||
| ) | |||
| Target_Link_Libraries ( | |||
| cppamqp | |||
| cpputils | |||
| ) | |||
| If ( __COTIRE_INCLUDED ) | |||
| Cotire ( cppamqp ) | |||
| EndIf ( ) | |||
| If ( __STRIP_SYMBOLS_INCLUDED AND BUILD_SHARED_LIBS ) | |||
| Strip_Symbols ( cppamqp DBG_FILE ) | |||
| EndIf () | |||
| @@ -0,0 +1,126 @@ | |||
| #include <cppamqp/helper.inl> | |||
| #include <cppamqp/channel.inl> | |||
| #include <cppamqp/exception.inl> | |||
| #include <cppamqp/connection.inl> | |||
| using namespace ::cppamqp; | |||
| channel::internal::internal(const cppamqp::connection& p_connection, channel_number p_handle) | |||
| : connection(p_connection) | |||
| , handle (p_handle) | |||
| { | |||
| if (handle <= 0) | |||
| throw utl::argument_exception("handle", "handle must be greater than 0"); | |||
| amqp_channel_open(connection.handle(), handle); | |||
| __impl::check_and_raise( | |||
| amqp_get_rpc_reply(connection.handle()), | |||
| std::string("unable to open channel ") + std::to_string(handle), | |||
| false); | |||
| } | |||
| channel::internal::~internal() | |||
| { | |||
| if (handle <= 0) | |||
| return; | |||
| __impl::check_and_raise( | |||
| amqp_channel_close(connection.handle(), handle, AMQP_REPLY_SUCCESS), | |||
| "unable to close amqp channel", | |||
| true); | |||
| } | |||
| queue_declaration channel::declare_queue(const std::string& name, const queue_flags& flags) | |||
| { | |||
| auto ret = amqp_queue_declare( | |||
| connection().handle(), | |||
| handle(), | |||
| __impl::make_bytes(name), | |||
| flags.is_set(queue_flag::passive) ? 1 : 0, | |||
| flags.is_set(queue_flag::durable) ? 1 : 0, | |||
| flags.is_set(queue_flag::exclusive) ? 1 : 0, | |||
| flags.is_set(queue_flag::auto_delete) ? 1 : 0, | |||
| amqp_empty_table); | |||
| __impl::check_and_raise( | |||
| amqp_get_rpc_reply(connection().handle()), | |||
| std::string("unable to declare queue '") + name + "'", | |||
| false); | |||
| if (!ret) | |||
| throw exception(std::string("unable to declare queue '") + name + "'"); | |||
| return queue_declaration | |||
| { | |||
| std::string(static_cast<const char*>(ret->queue.bytes), ret->queue.len), | |||
| ret->message_count, | |||
| ret->consumer_count | |||
| }; | |||
| } | |||
| void channel::bind_queue(const std::string& queue, const std::string& exchange, const std::string& routing_key) | |||
| { | |||
| amqp_queue_bind( | |||
| connection().handle(), | |||
| handle(), | |||
| __impl::make_bytes(queue), | |||
| __impl::make_bytes(exchange), | |||
| __impl::make_bytes(routing_key), | |||
| amqp_empty_table); | |||
| __impl::check_and_raise( | |||
| amqp_get_rpc_reply(connection().handle()), | |||
| static_cast<std::ostringstream&>(std::ostringstream() << | |||
| "unable to bind queue '" << queue << | |||
| "' to '" << exchange << | |||
| "' using '" << routing_key << | |||
| "'").str(), | |||
| false); | |||
| } | |||
| void channel::publish( | |||
| const std::string& exchange, | |||
| const std::string& routing_key, | |||
| const publish_flags& flags, | |||
| const std::string& message, | |||
| const publish_options* options) | |||
| { | |||
| __impl::check_and_raise( | |||
| amqp_basic_publish( | |||
| connection().handle(), | |||
| handle(), | |||
| __impl::make_bytes(exchange), | |||
| __impl::make_bytes(routing_key), | |||
| flags.is_set(publish_flag::mandatory) ? 1 : 0, | |||
| flags.is_set(publish_flag::immediate) ? 1 : 0, | |||
| options ? options->properties() : nullptr, | |||
| __impl::make_bytes(message)), | |||
| "error while publishing message", | |||
| false); | |||
| } | |||
| std::string channel::consume(const std::string& queue, const std::string& consumer_tag, const consume_flags& flags) | |||
| { | |||
| auto ret = amqp_basic_consume( | |||
| connection().handle(), | |||
| handle(), | |||
| __impl::make_bytes(queue), | |||
| __impl::make_bytes(consumer_tag), | |||
| flags.is_set(consume_flag::no_local) ? 1 : 0, | |||
| flags.is_set(consume_flag::no_ack) ? 1 : 0, | |||
| flags.is_set(consume_flag::exclusive) ? 1 : 0, | |||
| amqp_empty_table); | |||
| __impl::check_and_raise( | |||
| amqp_get_rpc_reply(connection().handle()), | |||
| static_cast<std::ostringstream&>(std::ostringstream() << "unable to consume from queue '" << queue << "'").str(), | |||
| false); | |||
| if (!ret) | |||
| throw exception(std::string("unable to consume from queue '") + queue + "'"); | |||
| return __impl::from_bytes(ret->consumer_tag); | |||
| } | |||
| void channel::close(int status) | |||
| { | |||
| if (!_internal) | |||
| return; | |||
| __impl::check_and_raise( | |||
| amqp_channel_close(connection().handle(), handle(), status), | |||
| "unable to close channel", | |||
| false); | |||
| _internal->handle = 0; | |||
| _internal.reset(); | |||
| } | |||
| @@ -0,0 +1,164 @@ | |||
| #include <cpputils/misc/time.h> | |||
| #include <cppamqp/helper.h> | |||
| #include <cppamqp/message.inl> | |||
| #include <cppamqp/exception.inl> | |||
| #include <cppamqp/connection.inl> | |||
| #include <cppamqp/consume_result.inl> | |||
| #include <cppamqp/publish_options.inl> | |||
| using namespace ::cppamqp; | |||
| connection::internal::internal() : | |||
| connection (amqp_new_connection()), | |||
| reply_code (AMQP_REPLY_SUCCESS) | |||
| { | |||
| if (!connection) | |||
| throw cppamqp::exception("unable to create connection"); | |||
| } | |||
| connection::internal::~internal() | |||
| { | |||
| if (auto_close) | |||
| { | |||
| auto_close = false; | |||
| __impl::check_and_raise( | |||
| amqp_connection_close(connection, reply_code), | |||
| "error while closing connection", | |||
| true); | |||
| } | |||
| __impl::check_and_raise( | |||
| amqp_destroy_connection(connection), | |||
| "error while destroying connection", | |||
| true); | |||
| } | |||
| struct internal_envelope : public amqp_envelope_t | |||
| { | |||
| internal_envelope() | |||
| { memset(this, 0, sizeof(*this)); } | |||
| ~internal_envelope() | |||
| { amqp_destroy_envelope(this); } | |||
| }; | |||
| struct internal_message : public amqp_message_t | |||
| { | |||
| internal_message() | |||
| { memset(this, 0, sizeof(*this)); } | |||
| ~internal_message() | |||
| { amqp_destroy_message(this); } | |||
| }; | |||
| void connection::tcp_connect(const std::string& hostname, uint port) | |||
| { | |||
| _internal.reset(new internal()); | |||
| auto socket = amqp_tcp_socket_new(handle()); | |||
| if (!socket) | |||
| throw exception(std::string("unable to open tcp socket to [") + hostname + "]:" + std::to_string(port)); | |||
| __impl::check_and_raise( | |||
| amqp_socket_open(socket, hostname.c_str(), static_cast<int>(port)), | |||
| "error while opening socket", | |||
| false); | |||
| _internal->auto_close = true; | |||
| } | |||
| void connection::login_plain( | |||
| const std::string& username, | |||
| const std::string& password, | |||
| const std::string& vhost, | |||
| int maxChannels, | |||
| int maxFrameSize) | |||
| { | |||
| __impl::check_and_raise( | |||
| amqp_login(handle(), vhost.c_str(), maxChannels, maxFrameSize, 0, AMQP_SASL_METHOD_PLAIN, username.c_str(), password.c_str()), | |||
| "error while login plain", | |||
| false); | |||
| } | |||
| void connection::login_external( | |||
| const std::string& identify, | |||
| const std::string& vhost, | |||
| int maxChannels, | |||
| int maxFrameSize) | |||
| { | |||
| __impl::check_and_raise( | |||
| amqp_login(handle(), vhost.c_str(), maxChannels, maxFrameSize, 0, AMQP_SASL_METHOD_EXTERNAL, identify.c_str()), | |||
| "error while login external", | |||
| false); | |||
| } | |||
| channel connection::open_channel(channel_number c) | |||
| { return channel(*this, c); } | |||
| consume_result connection::consume_message(const std::chrono::milliseconds& timeout) | |||
| { | |||
| ::timeval tv = utl::duration_cast<::timeval>(timeout); | |||
| internal_envelope envelope; | |||
| amqp_maybe_release_buffers(handle()); | |||
| auto ret = amqp_consume_message( | |||
| handle(), | |||
| &envelope, | |||
| timeout.count() >= 0 ? &tv : nullptr, | |||
| 0); | |||
| if (ret.reply_type == AMQP_RESPONSE_NORMAL) | |||
| return consume_result(*this, envelope); | |||
| if ( ret.reply_type == AMQP_RESPONSE_LIBRARY_EXCEPTION | |||
| && ret.library_error == AMQP_STATUS_TIMEOUT) // TODO: test this case!!!! | |||
| return consume_result(*this, consume_result_type::timeout); | |||
| if ( ret.reply_type != AMQP_RESPONSE_LIBRARY_EXCEPTION | |||
| || ret.library_error != AMQP_STATUS_UNEXPECTED_STATE) | |||
| __impl::check_and_raise(ret, "error while consuming message", false); | |||
| amqp_frame_t frame; | |||
| memset(&tv, 0, sizeof(tv)); | |||
| __impl::check_and_raise( | |||
| amqp_simple_wait_frame_noblock(handle(), &frame, &tv), | |||
| "error wile waiting for frame", | |||
| false); | |||
| if (frame.frame_type != AMQP_FRAME_METHOD) | |||
| throw exception("unexpected or unknown frame type"); | |||
| switch(frame.payload.method.id) | |||
| { | |||
| case AMQP_BASIC_ACK_METHOD: | |||
| return consume_result(*this, consume_result_type::acknowledge); | |||
| case AMQP_BASIC_RETURN_METHOD: | |||
| { | |||
| internal_message message; | |||
| __impl::check_and_raise( | |||
| amqp_read_message(handle(), frame.channel, &message, 0), | |||
| "error while reading returned message", | |||
| false); | |||
| return consume_result(*this, consume_result_type::could_not_deliver_return_to_sender, message); | |||
| } | |||
| case AMQP_CHANNEL_CLOSE_METHOD: | |||
| throw exception("channel is closed"); | |||
| case AMQP_CONNECTION_CLOSE_METHOD: | |||
| close(AMQP_REPLY_SUCCESS, true); | |||
| return consume_result(*this, consume_result_type::connection_closed_by_peer); | |||
| default: | |||
| throw exception("received unexpected frame method"); | |||
| } | |||
| } | |||
| void connection::close(int status, bool force) | |||
| { | |||
| if (!_internal) | |||
| return; | |||
| if (force) | |||
| _internal->auto_close = false; | |||
| _internal->reply_code = status; | |||
| _internal.reset(); | |||
| } | |||
| @@ -0,0 +1,57 @@ | |||
| #include <cppamqp/helper.h> | |||
| #include <cppamqp/exception.inl> | |||
| #include <cpputils/logging/global.h> | |||
| void ::cppamqp::__impl::check_and_raise(int returnCode, std::string msg, bool logOnly) | |||
| { | |||
| if (returnCode == AMQP_STATUS_OK) | |||
| return; | |||
| if (logOnly) | |||
| { | |||
| if (!msg.empty()) | |||
| msg += " - "; | |||
| msg += amqp_error_string2(returnCode); | |||
| log_global_message(error, msg); | |||
| } | |||
| else | |||
| throw cppamqp::exception(static_cast<amqp_status_enum>(returnCode)); | |||
| } | |||
| void ::cppamqp::__impl::check_and_raise(const amqp_rpc_reply_t& reply, std::string msg, bool logOnly) | |||
| { | |||
| switch(reply.reply_type) | |||
| { | |||
| case AMQP_RESPONSE_NORMAL: | |||
| return; | |||
| case AMQP_RESPONSE_SERVER_EXCEPTION: | |||
| { | |||
| if (!msg.empty()) | |||
| msg += " - "; | |||
| msg += std::string("Server error: ID=") + std::to_string(reply.reply.id); | |||
| if (logOnly) { | |||
| log_global_message(error, msg); | |||
| } else { | |||
| throw cppamqp::exception(msg); | |||
| } | |||
| } | |||
| break; | |||
| case AMQP_RESPONSE_LIBRARY_EXCEPTION: | |||
| check_and_raise(reply.library_error, msg, logOnly); | |||
| break; | |||
| default: | |||
| { | |||
| if (!msg.empty()) | |||
| msg += " - "; | |||
| msg += "unknown reply"; | |||
| if (logOnly) { | |||
| log_global_message(error, msg); | |||
| } else { | |||
| throw cppamqp::exception(msg); | |||
| } | |||
| } | |||
| break; | |||
| } | |||
| } | |||
| @@ -0,0 +1,58 @@ | |||
| #include <cppamqp/helper.inl> | |||
| #include <cppamqp/publish_options.h> | |||
| using namespace ::cppamqp; | |||
| template<class T, class S> | |||
| inline T simpleConvert(const S& value) | |||
| { return static_cast<T>(value); } | |||
| template<> | |||
| inline amqp_bytes_t simpleConvert<amqp_bytes_t, std::string>(const std::string& value) | |||
| { return __impl::make_bytes(value); } | |||
| template<class T, class S> | |||
| inline void addProperty(amqp_basic_properties_t& properties, amqp_flags_t flag, T amqp_basic_properties_t::*member, const utl::nullable<S>& value) | |||
| { | |||
| if (value) | |||
| { | |||
| properties.*member = simpleConvert<T, S>(value()); | |||
| properties._flags |= flag; | |||
| } | |||
| } | |||
| const amqp_basic_properties_t* publish_options::properties() const | |||
| { | |||
| _properties._flags = 0; | |||
| addProperty(_properties, AMQP_BASIC_CONTENT_TYPE_FLAG, &amqp_basic_properties_t::content_type, content_type); | |||
| addProperty(_properties, AMQP_BASIC_CONTENT_ENCODING_FLAG, &amqp_basic_properties_t::content_encoding, content_encoding); | |||
| addProperty(_properties, AMQP_BASIC_DELIVERY_MODE_FLAG, &amqp_basic_properties_t::delivery_mode, delivery_mode); | |||
| addProperty(_properties, AMQP_BASIC_PRIORITY_FLAG, &amqp_basic_properties_t::priority, priority); | |||
| addProperty(_properties, AMQP_BASIC_CORRELATION_ID_FLAG, &amqp_basic_properties_t::correlation_id, correlation_id); | |||
| addProperty(_properties, AMQP_BASIC_REPLY_TO_FLAG, &amqp_basic_properties_t::reply_to, reply_to); | |||
| addProperty(_properties, AMQP_BASIC_EXPIRATION_FLAG, &amqp_basic_properties_t::expiration, expiration); | |||
| addProperty(_properties, AMQP_BASIC_MESSAGE_ID_FLAG, &amqp_basic_properties_t::message_id, message_id); | |||
| addProperty(_properties, AMQP_BASIC_TIMESTAMP_FLAG, &amqp_basic_properties_t::timestamp, timestamp); | |||
| addProperty(_properties, AMQP_BASIC_TYPE_FLAG, &amqp_basic_properties_t::type, type); | |||
| addProperty(_properties, AMQP_BASIC_USER_ID_FLAG, &amqp_basic_properties_t::user_id, user_id); | |||
| addProperty(_properties, AMQP_BASIC_APP_ID_FLAG, &amqp_basic_properties_t::app_id, app_id); | |||
| addProperty(_properties, AMQP_BASIC_CLUSTER_ID_FLAG, &amqp_basic_properties_t::cluster_id, cluster_id); | |||
| return &_properties; | |||
| } | |||
| void publish_options::reset() | |||
| { | |||
| content_type.reset(); | |||
| content_encoding.reset(); | |||
| delivery_mode.reset(); | |||
| priority.reset(); | |||
| correlation_id.reset(); | |||
| reply_to.reset(); | |||
| expiration.reset(); | |||
| message_id.reset(); | |||
| timestamp.reset(); | |||
| type.reset(); | |||
| user_id.reset(); | |||
| app_id.reset(); | |||
| cluster_id.reset(); | |||
| } | |||
| @@ -0,0 +1,29 @@ | |||
| # Initialize ###################################################################################### | |||
| Include ( cotire OPTIONAL ) | |||
| Include ( pedantic OPTIONAL ) | |||
| Include ( cmake_tests OPTIONAL ) | |||
| Set ( CMAKE_CXX_STANDARD 17 ) | |||
| Set ( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${PEDANTIC_C_FLAGS}" ) | |||
| Set ( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${PEDANTIC_CXX_FLAGS}" ) | |||
| # Project: test_cppamqp ########################################################################## | |||
| Project ( test_cppamqp ) | |||
| File ( GLOB_RECURSE SOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp ) | |||
| Add_Executable ( test_cppamqp EXCLUDE_FROM_ALL ${SOURCE_FILES} ) | |||
| Target_Link_Libraries ( | |||
| test_cppamqp | |||
| cppamqp | |||
| gtest | |||
| gmock | |||
| gmock_main | |||
| pthread | |||
| ) | |||
| If ( __COTIRE_INCLUDED ) | |||
| Cotire ( test_cppamqp ) | |||
| EndIf ( ) | |||
| If ( __CMAKE_TESTS_INCLUDED ) | |||
| Add_CMake_Test ( cppamqp test_cppamqp ) | |||
| EndIf ( ) | |||
| @@ -0,0 +1,722 @@ | |||
| #include <gtest/gtest.h> | |||
| #include <cppamqp.h> | |||
| #include "mock.h" | |||
| using namespace ::testing; | |||
| using namespace ::cppamqp; | |||
| using StrictAmqpMock = StrictMock<AmqpMock>; | |||
| amqp_envelope_t DefaultEnvelope { | |||
| /* .channel */ 3, | |||
| /* .consumer_tag */ { 15, const_cast<char*>("my_consumer_tag") }, | |||
| /* .delivery_tag */ 12345678, | |||
| /* .redelivered */ true, | |||
| /* .exchange */ { 11, const_cast<char*>("my_exchange") }, | |||
| /* .routing_key */ { 14, const_cast<char*>("my_routing_key") }, | |||
| /* .message */ { | |||
| /* .properties */ { | |||
| /* ._flags */ 0, | |||
| /* .content_type */ { 0, nullptr }, | |||
| /* .content_encoding */ { 0, nullptr }, | |||
| /* .headers */ { 0, nullptr }, | |||
| /* .delivery_mode */ 0, | |||
| /* .priority */ 0, | |||
| /* .correlation_id */ { 0, nullptr }, | |||
| /* .reply_to */ { 0, nullptr }, | |||
| /* .expiration */ { 0, nullptr }, | |||
| /* .message_id */ { 0, nullptr }, | |||
| /* .timestamp */ 0, | |||
| /* .type */ { 0, nullptr }, | |||
| /* .user_id */ { 0, nullptr }, | |||
| /* .app_id */ { 0, nullptr }, | |||
| /* .cluster_id */ { 0, nullptr }, | |||
| }, | |||
| /* .body */ { 19, const_cast<char*>("this_is_the_message") }, | |||
| /* .pool */ { | |||
| /* .pagesize */ 0, | |||
| /* .pages */ { 0, nullptr }, | |||
| /* .large_blocks */ { 0, nullptr }, | |||
| /* .next_page */ 0, | |||
| /* .alloc_block */ nullptr, | |||
| /* .alloc_used */ 0 | |||
| } | |||
| } | |||
| }; | |||
| bool operator ==(const timeval& a, const timeval& b) | |||
| { return a.tv_sec == b.tv_sec && a.tv_usec == b.tv_usec; } | |||
| MATCHER_P(BytesEq, data, "") | |||
| { | |||
| if ( data == nullptr | |||
| && arg.len == 0) | |||
| return true; | |||
| std::string d(data); | |||
| return arg.len == d.size() | |||
| && memcmp(arg.bytes, d.data(), arg.len) == 0; | |||
| } | |||
| TEST(AmqpTest, Connection_connect_newConnectionFailed) | |||
| { | |||
| StrictAmqpMock mock; | |||
| InSequence seq; | |||
| EXPECT_CALL(mock, amqp_new_connection()) | |||
| .WillOnce(Return(nullptr)); | |||
| connection con; | |||
| EXPECT_THROW(con.tcp_connect("localhost", 1234), cppamqp::exception); | |||
| } | |||
| TEST(AmqpTest, Connection_connect_newSocketFailed) | |||
| { | |||
| StrictAmqpMock mock; | |||
| InSequence seq; | |||
| EXPECT_CALL(mock, amqp_new_connection()) | |||
| .WillOnce(Return(reinterpret_cast<amqp_connection_state_t>(1))); | |||
| EXPECT_CALL(mock, amqp_tcp_socket_new(reinterpret_cast<amqp_connection_state_t>(1))) | |||
| .WillOnce(Return(nullptr)); | |||
| EXPECT_CALL(mock, amqp_destroy_connection(reinterpret_cast<amqp_connection_state_t>(1))) | |||
| .WillOnce(Return(0)); | |||
| connection con; | |||
| EXPECT_THROW(con.tcp_connect("localhost", 1234), cppamqp::exception); | |||
| } | |||
| TEST(AmqpTest, Connection_connect_openSocketFailed) | |||
| { | |||
| StrictAmqpMock mock; | |||
| InSequence seq; | |||
| EXPECT_CALL(mock, amqp_new_connection()) | |||
| .WillOnce(Return(reinterpret_cast<amqp_connection_state_t>(1))); | |||
| EXPECT_CALL(mock, amqp_tcp_socket_new(reinterpret_cast<amqp_connection_state_t>(1))) | |||
| .WillOnce(Return(reinterpret_cast<amqp_socket_t*>(2))); | |||
| EXPECT_CALL(mock, amqp_socket_open(reinterpret_cast<amqp_socket_t*>(2), StrEq("localhost"), 1234)) | |||
| .WillOnce(Return(666)); | |||
| EXPECT_CALL(mock, amqp_error_string2(666)) | |||
| .WillOnce(Return("error from hell >:D")); | |||
| EXPECT_CALL(mock, amqp_destroy_connection(reinterpret_cast<amqp_connection_state_t>(1))) | |||
| .WillOnce(Return(0)); | |||
| connection con; | |||
| EXPECT_THROW(con.tcp_connect("localhost", 1234), cppamqp::exception); | |||
| } | |||
| TEST(AmqpTest, Connection_connect_success) | |||
| { | |||
| StrictAmqpMock mock; | |||
| InSequence seq; | |||
| EXPECT_CALL(mock, amqp_new_connection()) | |||
| .WillOnce(Return(reinterpret_cast<amqp_connection_state_t>(1))); | |||
| EXPECT_CALL(mock, amqp_tcp_socket_new(reinterpret_cast<amqp_connection_state_t>(1))) | |||
| .WillOnce(Return(reinterpret_cast<amqp_socket_t*>(2))); | |||
| EXPECT_CALL(mock, amqp_socket_open(reinterpret_cast<amqp_socket_t*>(2), StrEq("localhost"), 1234)) | |||
| .WillOnce(Return(0)); | |||
| EXPECT_CALL(mock, amqp_connection_close(reinterpret_cast<amqp_connection_state_t>(1), 200)) | |||
| .WillOnce(Return(AmqpMock::defaultRpcReply)); | |||
| EXPECT_CALL(mock, amqp_destroy_connection(reinterpret_cast<amqp_connection_state_t>(1))) | |||
| .WillOnce(Return(0)); | |||
| connection con; | |||
| EXPECT_NO_THROW(con.tcp_connect("localhost", 1234)); | |||
| } | |||
| TEST(AmqpTest, Connection_loginPlain_failed) | |||
| { | |||
| connection con; | |||
| con.tcp_connect("localhost", 1234); | |||
| StrictAmqpMock mock; | |||
| InSequence seq; | |||
| EXPECT_CALL(mock, amqp_login_plain(AmqpMock::defaultConnectionState, StrEq("vhost"), 100, 200, 0, AMQP_SASL_METHOD_PLAIN, StrEq("username"), StrEq("password"))) | |||
| .WillOnce(Return(amqp_rpc_reply_t { | |||
| static_cast<amqp_response_type_enum>(AMQP_RESPONSE_LIBRARY_EXCEPTION), | |||
| { 0, nullptr }, | |||
| 666 })); | |||
| EXPECT_CALL(mock, amqp_error_string2(666)) | |||
| .WillOnce(Return("error from hell >:D")); | |||
| EXPECT_THROW(con.login_plain("username", "password", "vhost", 100, 200), cppamqp::exception); | |||
| } | |||
| TEST(AmqpTest, Connection_loginPlain_success) | |||
| { | |||
| connection con; | |||
| con.tcp_connect("localhost", 1234); | |||
| StrictAmqpMock mock; | |||
| InSequence seq; | |||
| EXPECT_CALL(mock, amqp_login_plain(AmqpMock::defaultConnectionState, StrEq("vhost"), 100, 200, 0, AMQP_SASL_METHOD_PLAIN, StrEq("username"), StrEq("password"))) | |||
| .WillOnce(Return(AmqpMock::defaultRpcReply)); | |||
| EXPECT_NO_THROW(con.login_plain("username", "password", "vhost", 100, 200)); | |||
| } | |||
| TEST(AmqpTest, Connection_openChannel_failed) | |||
| { | |||
| std::string channelId("test_channel"); | |||
| amqp_channel_open_ok_t channelOk { { | |||
| channelId.size(), | |||
| const_cast<char*>(channelId.c_str()) | |||
| } }; | |||
| connection con; | |||
| con.tcp_connect("localhost", 1234); | |||
| StrictAmqpMock mock; | |||
| InSequence seq; | |||
| EXPECT_CALL(mock, amqp_channel_open(AmqpMock::defaultConnectionState, 3)) | |||
| .WillOnce(Return(&channelOk)); | |||
| EXPECT_CALL(mock, amqp_get_rpc_reply(AmqpMock::defaultConnectionState)) | |||
| .WillOnce(Return(amqp_rpc_reply_t { | |||
| static_cast<amqp_response_type_enum>(AMQP_RESPONSE_LIBRARY_EXCEPTION), | |||
| { 0, nullptr }, | |||
| 666 })); | |||
| EXPECT_CALL(mock, amqp_error_string2(666)) | |||
| .WillOnce(Return("error from hell >:D")); | |||
| EXPECT_THROW(con.open_channel(3), cppamqp::exception); | |||
| } | |||
| TEST(AmqpTest, Connection_openChannel_success) | |||
| { | |||
| std::string channelId("test_channel"); | |||
| amqp_channel_open_ok_t channelOk { { | |||
| channelId.size(), | |||
| const_cast<char*>(channelId.c_str()) | |||
| } }; | |||
| connection con; | |||
| con.tcp_connect("localhost", 1234); | |||
| StrictAmqpMock mock; | |||
| InSequence seq; | |||
| EXPECT_CALL(mock, amqp_channel_open(AmqpMock::defaultConnectionState, 3)) | |||
| .WillOnce(Return(&channelOk)); | |||
| EXPECT_CALL(mock, amqp_get_rpc_reply(AmqpMock::defaultConnectionState)) | |||
| .WillOnce(Return(AmqpMock::defaultRpcReply)); | |||
| EXPECT_CALL(mock, amqp_channel_close(AmqpMock::defaultConnectionState, 3, AMQP_REPLY_SUCCESS)) | |||
| .WillOnce(Return(AmqpMock::defaultRpcReply)); | |||
| EXPECT_NO_THROW(con.open_channel(3)); | |||
| } | |||
| TEST(AmqpTest, Connection_consumeMessage_success) | |||
| { | |||
| connection con; | |||
| con.tcp_connect("localhost", 1234); | |||
| StrictAmqpMock mock; | |||
| InSequence seq; | |||
| EXPECT_CALL(mock, amqp_maybe_release_buffers(AmqpMock::defaultConnectionState)) | |||
| .Times(1); | |||
| EXPECT_CALL(mock, amqp_consume_message(AmqpMock::defaultConnectionState, NotNull(), nullptr, 0)) | |||
| .WillOnce(DoAll(SetArgPointee<1>(DefaultEnvelope), Return(AmqpMock::defaultRpcReply))); | |||
| EXPECT_CALL(mock, amqp_destroy_envelope(NotNull())) | |||
| .Times(1); | |||
| auto mr = con.consume_message(); | |||
| EXPECT_EQ(consume_result_type::success, mr.type); | |||
| EXPECT_EQ(std::string("this_is_the_message"), mr.message.body); | |||
| EXPECT_EQ(3, mr.channel); | |||
| EXPECT_EQ(std::string("my_consumer_tag"), mr.consumer_tag); | |||
| EXPECT_EQ(12345678, mr.delivery_tag); | |||
| EXPECT_EQ(true, mr.redelivered); | |||
| EXPECT_EQ(std::string("my_exchange"), mr.exchange); | |||
| EXPECT_EQ(std::string("my_routing_key"), mr.routing_key); | |||
| } | |||
| TEST(AmqpTest, Connection_consumeMessage_nonLibraryError) | |||
| { | |||
| connection con; | |||
| con.tcp_connect("localhost", 1234); | |||
| StrictAmqpMock mock; | |||
| InSequence seq; | |||
| EXPECT_CALL(mock, amqp_maybe_release_buffers(AmqpMock::defaultConnectionState)) | |||
| .Times(1); | |||
| EXPECT_CALL(mock, amqp_consume_message(AmqpMock::defaultConnectionState, NotNull(), nullptr, 0)) | |||
| .WillOnce( | |||
| DoAll(SetArgPointee<1>(DefaultEnvelope), | |||
| Return(amqp_rpc_reply_t { | |||
| static_cast<amqp_response_type_enum>(AMQP_RESPONSE_SERVER_EXCEPTION), | |||
| { 0, nullptr }, | |||
| 666 }))); | |||
| EXPECT_CALL(mock, amqp_destroy_envelope(NotNull())) | |||
| .Times(1); | |||
| EXPECT_THROW(con.consume_message(), cppamqp::exception); | |||
| } | |||
| TEST(AmqpTest, Connection_consumeMessage_nonUnexpectedState) | |||
| { | |||
| connection con; | |||
| con.tcp_connect("localhost", 1234); | |||
| StrictAmqpMock mock; | |||
| InSequence seq; | |||
| EXPECT_CALL(mock, amqp_maybe_release_buffers(AmqpMock::defaultConnectionState)) | |||
| .Times(1); | |||
| EXPECT_CALL(mock, amqp_consume_message(AmqpMock::defaultConnectionState, NotNull(), nullptr, 0)) | |||
| .WillOnce( | |||
| DoAll(SetArgPointee<1>(DefaultEnvelope), | |||
| Return(amqp_rpc_reply_t { | |||
| static_cast<amqp_response_type_enum>(AMQP_RESPONSE_LIBRARY_EXCEPTION), | |||
| { 0, nullptr }, | |||
| 666 }))); | |||
| EXPECT_CALL(mock, amqp_error_string2(666)) | |||
| .WillOnce(Return("error from hell >:D")); | |||
| EXPECT_CALL(mock, amqp_destroy_envelope(NotNull())) | |||
| .Times(1); | |||
| EXPECT_THROW(con.consume_message(), cppamqp::exception); | |||
| } | |||
| TEST(AmqpTest, Connection_consumeMessage_waitFrameError) | |||
| { | |||
| connection con; | |||
| con.tcp_connect("localhost", 1234); | |||
| StrictAmqpMock mock; | |||
| InSequence seq; | |||
| EXPECT_CALL(mock, amqp_maybe_release_buffers(AmqpMock::defaultConnectionState)) | |||
| .Times(1); | |||
| EXPECT_CALL(mock, amqp_consume_message(AmqpMock::defaultConnectionState, NotNull(), nullptr, 0)) | |||
| .WillOnce(DoAll( | |||
| SetArgPointee<1>(DefaultEnvelope), | |||
| Return(amqp_rpc_reply_t { | |||
| static_cast<amqp_response_type_enum>(AMQP_RESPONSE_LIBRARY_EXCEPTION), | |||
| { 0, nullptr }, | |||
| AMQP_STATUS_UNEXPECTED_STATE }))); | |||
| EXPECT_CALL(mock, amqp_simple_wait_frame_noblock(AmqpMock::defaultConnectionState, NotNull(), Pointee(timeval { 0, 0 }))) | |||
| .WillOnce(Return(666)); | |||
| EXPECT_CALL(mock, amqp_error_string2(666)) | |||
| .WillOnce(Return("error from hell >:D")); | |||
| EXPECT_CALL(mock, amqp_destroy_envelope(NotNull())) | |||
| .Times(1); | |||
| EXPECT_THROW(con.consume_message(), cppamqp::exception); | |||
| } | |||
| TEST(AmqpTest, Connection_consumeMessage_noMethodFrame) | |||
| { | |||
| amqp_frame_t frame { | |||
| /* .frame_type */ AMQP_FRAME_HEADER, | |||
| /* .channel */ 3, | |||
| /* .payload */ { { 0, nullptr } } | |||
| }; | |||
| connection con; | |||
| con.tcp_connect("localhost", 1234); | |||
| StrictAmqpMock mock; | |||
| InSequence seq; | |||
| EXPECT_CALL(mock, amqp_maybe_release_buffers(AmqpMock::defaultConnectionState)) | |||
| .Times(1); | |||
| EXPECT_CALL(mock, amqp_consume_message(AmqpMock::defaultConnectionState, NotNull(), nullptr, 0)) | |||
| .WillOnce(DoAll( | |||
| SetArgPointee<1>(DefaultEnvelope), | |||
| Return(amqp_rpc_reply_t { | |||
| static_cast<amqp_response_type_enum>(AMQP_RESPONSE_LIBRARY_EXCEPTION), | |||
| { 0, nullptr }, | |||
| AMQP_STATUS_UNEXPECTED_STATE }))); | |||
| EXPECT_CALL(mock, amqp_simple_wait_frame_noblock(AmqpMock::defaultConnectionState, NotNull(), Pointee(timeval { 0, 0 }))) | |||
| .WillOnce(DoAll( | |||
| SetArgPointee<1>(frame), | |||
| Return(0))); | |||
| EXPECT_CALL(mock, amqp_destroy_envelope(NotNull())) | |||
| .Times(1); | |||
| EXPECT_THROW(con.consume_message(), cppamqp::exception); | |||
| } | |||
| TEST(AmqpTest, Connection_consumeMessage_ackMessage) | |||
| { | |||
| amqp_frame_t frame { | |||
| /* .frame_type */ AMQP_FRAME_METHOD, | |||
| /* .channel */ 3, | |||
| /* .payload */ { { AMQP_BASIC_ACK_METHOD, nullptr } } | |||
| }; | |||
| connection con; | |||
| con.tcp_connect("localhost", 1234); | |||
| StrictAmqpMock mock; | |||
| InSequence seq; | |||
| EXPECT_CALL(mock, amqp_maybe_release_buffers(AmqpMock::defaultConnectionState)) | |||
| .Times(1); | |||
| EXPECT_CALL(mock, amqp_consume_message(AmqpMock::defaultConnectionState, NotNull(), nullptr, 0)) | |||
| .WillOnce(DoAll( | |||
| SetArgPointee<1>(DefaultEnvelope), | |||
| Return(amqp_rpc_reply_t { | |||
| static_cast<amqp_response_type_enum>(AMQP_RESPONSE_LIBRARY_EXCEPTION), | |||
| { 0, nullptr }, | |||
| AMQP_STATUS_UNEXPECTED_STATE }))); | |||
| EXPECT_CALL(mock, amqp_simple_wait_frame_noblock(AmqpMock::defaultConnectionState, NotNull(), Pointee(timeval { 0, 0 }))) | |||
| .WillOnce(DoAll( | |||
| SetArgPointee<1>(frame), | |||
| Return(0))); | |||
| EXPECT_CALL(mock, amqp_destroy_envelope(NotNull())) | |||
| .Times(1); | |||
| auto mr = con.consume_message(); | |||
| EXPECT_EQ(consume_result_type::acknowledge, mr.type); | |||
| } | |||
| TEST(AmqpTest, Connection_consumeMessage_channelClosedMessage) | |||
| { | |||
| amqp_frame_t frame { | |||
| /* .frame_type */ AMQP_FRAME_METHOD, | |||
| /* .channel */ 3, | |||
| /* .payload */ { { AMQP_CHANNEL_CLOSE_METHOD, nullptr } } | |||
| }; | |||
| connection con; | |||
| con.tcp_connect("localhost", 1234); | |||
| StrictAmqpMock mock; | |||
| InSequence seq; | |||
| EXPECT_CALL(mock, amqp_maybe_release_buffers(AmqpMock::defaultConnectionState)) | |||
| .Times(1); | |||
| EXPECT_CALL(mock, amqp_consume_message(AmqpMock::defaultConnectionState, NotNull(), nullptr, 0)) | |||
| .WillOnce(DoAll( | |||
| SetArgPointee<1>(DefaultEnvelope), | |||
| Return(amqp_rpc_reply_t { | |||
| static_cast<amqp_response_type_enum>(AMQP_RESPONSE_LIBRARY_EXCEPTION), | |||
| { 0, nullptr }, | |||
| AMQP_STATUS_UNEXPECTED_STATE }))); | |||
| EXPECT_CALL(mock, amqp_simple_wait_frame_noblock(AmqpMock::defaultConnectionState, NotNull(), Pointee(timeval { 0, 0 }))) | |||
| .WillOnce(DoAll( | |||
| SetArgPointee<1>(frame), | |||
| Return(0))); | |||
| EXPECT_CALL(mock, amqp_destroy_envelope(NotNull())) | |||
| .Times(1); | |||
| EXPECT_THROW(con.consume_message(), cppamqp::exception); | |||
| } | |||
| TEST(AmqpTest, Connection_consumeMessage_connectionClosedMessage) | |||
| { | |||
| amqp_frame_t frame { | |||
| /* .frame_type */ AMQP_FRAME_METHOD, | |||
| /* .channel */ 3, | |||
| /* .payload */ { { AMQP_CONNECTION_CLOSE_METHOD, nullptr } } | |||
| }; | |||
| connection con; | |||
| con.tcp_connect("localhost", 1234); | |||
| StrictAmqpMock mock; | |||
| InSequence seq; | |||
| EXPECT_CALL(mock, amqp_maybe_release_buffers(AmqpMock::defaultConnectionState)) | |||
| .Times(1); | |||
| EXPECT_CALL(mock, amqp_consume_message(AmqpMock::defaultConnectionState, NotNull(), nullptr, 0)) | |||
| .WillOnce(DoAll( | |||
| SetArgPointee<1>(DefaultEnvelope), | |||
| Return(amqp_rpc_reply_t { | |||
| static_cast<amqp_response_type_enum>(AMQP_RESPONSE_LIBRARY_EXCEPTION), | |||
| { 0, nullptr }, | |||
| AMQP_STATUS_UNEXPECTED_STATE }))); | |||
| EXPECT_CALL(mock, amqp_simple_wait_frame_noblock(AmqpMock::defaultConnectionState, NotNull(), Pointee(timeval { 0, 0 }))) | |||
| .WillOnce(DoAll( | |||
| SetArgPointee<1>(frame), | |||
| Return(0))); | |||
| EXPECT_CALL(mock, amqp_destroy_connection(AmqpMock::defaultConnectionState)) | |||
| .WillOnce(Return(0)); | |||
| EXPECT_CALL(mock, amqp_destroy_envelope(NotNull())) | |||
| .Times(1); | |||
| auto mr = con.consume_message(); | |||
| EXPECT_EQ(consume_result_type::connection_closed_by_peer, mr.type); | |||
| } | |||
| TEST(AmqpTest, Connection_consumeMessage_returnToSender) | |||
| { | |||
| amqp_frame_t frame { | |||
| /* .frame_type */ AMQP_FRAME_METHOD, | |||
| /* .channel */ 3, | |||
| /* .payload */ { { AMQP_BASIC_RETURN_METHOD, nullptr } } | |||
| }; | |||
| connection con; | |||
| con.tcp_connect("localhost", 1234); | |||
| StrictAmqpMock mock; | |||
| InSequence seq; | |||
| EXPECT_CALL(mock, amqp_maybe_release_buffers(AmqpMock::defaultConnectionState)) | |||
| .Times(1); | |||
| EXPECT_CALL(mock, amqp_consume_message(AmqpMock::defaultConnectionState, NotNull(), nullptr, 0)) | |||
| .WillOnce(DoAll( | |||
| SetArgPointee<1>(DefaultEnvelope), | |||
| Return(amqp_rpc_reply_t { | |||
| static_cast<amqp_response_type_enum>(AMQP_RESPONSE_LIBRARY_EXCEPTION), | |||
| { 0, nullptr }, | |||
| AMQP_STATUS_UNEXPECTED_STATE }))); | |||
| EXPECT_CALL(mock, amqp_simple_wait_frame_noblock(AmqpMock::defaultConnectionState, NotNull(), Pointee(timeval { 0, 0 }))) | |||
| .WillOnce(DoAll( | |||
| SetArgPointee<1>(frame), | |||
| Return(0))); | |||
| EXPECT_CALL(mock, amqp_read_message(AmqpMock::defaultConnectionState, 3, NotNull(), 0)) | |||
| .WillOnce(DoAll( | |||
| SetArgPointee<2>(DefaultEnvelope.message), | |||
| Return(AmqpMock::defaultRpcReply))); | |||
| EXPECT_CALL(mock, amqp_destroy_message(NotNull())) | |||
| .Times(1); | |||
| EXPECT_CALL(mock, amqp_destroy_envelope(NotNull())) | |||
| .Times(1); | |||
| auto mr = con.consume_message(); | |||
| EXPECT_EQ(consume_result_type::could_not_deliver_return_to_sender, mr.type); | |||
| EXPECT_EQ(std::string("this_is_the_message"), mr.message.body); | |||
| } | |||
| TEST(AmqpTest, Connection_close_success) | |||
| { | |||
| connection con; | |||
| con.tcp_connect("localhost", 1234); | |||
| StrictAmqpMock mock; | |||
| InSequence seq; | |||
| EXPECT_CALL(mock, amqp_connection_close(AmqpMock::defaultConnectionState, 123)) | |||
| .WillOnce(Return(AmqpMock::defaultRpcReply)); | |||
| EXPECT_CALL(mock, amqp_destroy_connection(AmqpMock::defaultConnectionState)) | |||
| .WillOnce(Return(0)); | |||
| con.close(123); | |||
| } | |||
| TEST(AmqpTest, Connection_closeForced_success) | |||
| { | |||
| connection con; | |||
| con.tcp_connect("localhost", 1234); | |||
| StrictAmqpMock mock; | |||
| InSequence seq; | |||
| EXPECT_CALL(mock, amqp_destroy_connection(AmqpMock::defaultConnectionState)) | |||
| .WillOnce(Return(0)); | |||
| con.close(123, true); | |||
| } | |||
| TEST(AmqpTest, Channel_declareQueue_declareFailed) | |||
| { | |||
| connection con; | |||
| con.tcp_connect("localhost", 1234); | |||
| channel channel = con.open_channel(3); | |||
| StrictAmqpMock mock; | |||
| InSequence seq; | |||
| EXPECT_CALL(mock, amqp_queue_declare(AmqpMock::defaultConnectionState, 3, BytesEq("test_queue"), false, true, false, true, _)) | |||
| .WillOnce(Return(nullptr)); | |||
| EXPECT_CALL(mock, amqp_get_rpc_reply(AmqpMock::defaultConnectionState)) | |||
| .WillOnce(Return(AmqpMock::defaultRpcReply)); | |||
| EXPECT_THROW(channel.declare_queue("test_queue", queue_flags({ queue_flag::durable, queue_flag::auto_delete })), cppamqp::exception); | |||
| } | |||
| TEST(AmqpTest, Channel_declareQueue_rpcReplyError) | |||
| { | |||
| connection con; | |||
| con.tcp_connect("localhost", 1234); | |||
| channel channel = con.open_channel(3); | |||
| StrictAmqpMock mock; | |||
| InSequence seq; | |||
| EXPECT_CALL(mock, amqp_queue_declare(AmqpMock::defaultConnectionState, 3, BytesEq("test_queue"), true, false, false, true, _)) | |||
| .WillOnce(Return(nullptr)); | |||
| EXPECT_CALL(mock, amqp_get_rpc_reply(AmqpMock::defaultConnectionState)) | |||
| .WillOnce(Return(amqp_rpc_reply_t { | |||
| static_cast<amqp_response_type_enum>(AMQP_RESPONSE_LIBRARY_EXCEPTION), | |||
| { 0, nullptr }, | |||
| 666 })); | |||
| EXPECT_CALL(mock, amqp_error_string2(666)) | |||
| .WillOnce(Return("error from hell >:D")); | |||
| EXPECT_THROW(channel.declare_queue("test_queue", queue_flags({ queue_flag::passive, queue_flag::auto_delete })), cppamqp::exception); | |||
| } | |||
| TEST(AmqpTest, Channel_declareQueue_success) | |||
| { | |||
| std::string name("blub"); | |||
| amqp_queue_declare_ok_t queueDeclareOk { | |||
| { name.size(), const_cast<char*>(name.data()) }, | |||
| 123, | |||
| 456 | |||
| }; | |||
| connection con; | |||
| con.tcp_connect("localhost", 1234); | |||
| channel channel = con.open_channel(3); | |||
| StrictAmqpMock mock; | |||
| InSequence seq; | |||
| EXPECT_CALL(mock, amqp_queue_declare(AmqpMock::defaultConnectionState, 3, BytesEq("test_queue"), true, false, true, false, _)) | |||
| .WillOnce(Return(&queueDeclareOk)); | |||
| EXPECT_CALL(mock, amqp_get_rpc_reply(AmqpMock::defaultConnectionState)) | |||
| .WillOnce(Return(AmqpMock::defaultRpcReply)); | |||
| auto qDecl = channel.declare_queue("test_queue", queue_flags({ queue_flag::passive, queue_flag::exclusive })); | |||
| EXPECT_EQ(name, qDecl.name); | |||
| EXPECT_EQ(123, qDecl.message_count); | |||
| EXPECT_EQ(456, qDecl.consumer_count); | |||
| } | |||
| TEST(AmqpTest, Channel_bindQueue_rpcReplyError) | |||
| { | |||
| amqp_queue_bind_ok_t dummy { 0 }; | |||
| connection con; | |||
| con.tcp_connect("localhost", 1234); | |||
| channel channel = con.open_channel(3); | |||
| StrictAmqpMock mock; | |||
| InSequence seq; | |||
| EXPECT_CALL(mock, amqp_queue_bind(AmqpMock::defaultConnectionState, 3, BytesEq("test_queue"), BytesEq("my_exchange"), BytesEq("the_routing_key"), _)) | |||
| .WillOnce(Return(&dummy)); | |||
| EXPECT_CALL(mock, amqp_get_rpc_reply(AmqpMock::defaultConnectionState)) | |||
| .WillOnce(Return(amqp_rpc_reply_t { | |||
| static_cast<amqp_response_type_enum>(AMQP_RESPONSE_LIBRARY_EXCEPTION), | |||
| { 0, nullptr }, | |||
| 666 })); | |||
| EXPECT_CALL(mock, amqp_error_string2(666)) | |||
| .WillOnce(Return("error from hell >:D")); | |||
| EXPECT_THROW(channel.bind_queue("test_queue", "my_exchange", "the_routing_key"), cppamqp::exception); | |||
| } | |||
| TEST(AmqpTest, Channel_bindQueue_success) | |||
| { | |||
| amqp_queue_bind_ok_t dummy { 0 }; | |||
| connection con; | |||
| con.tcp_connect("localhost", 1234); | |||
| channel channel = con.open_channel(3); | |||
| StrictAmqpMock mock; | |||
| InSequence seq; | |||
| EXPECT_CALL(mock, amqp_queue_bind(AmqpMock::defaultConnectionState, 3, BytesEq("test_queue"), BytesEq("my_exchange"), BytesEq("the_routing_key"), _)) | |||
| .WillOnce(Return(&dummy)); | |||
| EXPECT_CALL(mock, amqp_get_rpc_reply(AmqpMock::defaultConnectionState)) | |||
| .WillOnce(Return(AmqpMock::defaultRpcReply)); | |||
| EXPECT_NO_THROW(channel.bind_queue("test_queue", "my_exchange", "the_routing_key")); | |||
| } | |||
| TEST(AmqpTest, Channel_publish_failed) | |||
| { | |||
| connection con; | |||
| con.tcp_connect("localhost", 1234); | |||
| channel channel = con.open_channel(3); | |||
| StrictAmqpMock mock; | |||
| InSequence seq; | |||
| EXPECT_CALL(mock, amqp_basic_publish(AmqpMock::defaultConnectionState, 3, BytesEq("my_exchange"), BytesEq("my_routing_key"), false, true, nullptr, BytesEq("the_message"))) | |||
| .WillOnce(Return(666)); | |||
| EXPECT_CALL(mock, amqp_error_string2(666)) | |||
| .WillOnce(Return("error from hell >:D")); | |||
| EXPECT_THROW(channel.publish("my_exchange", "my_routing_key", publish_flags({ publish_flag::immediate }), "the_message", nullptr), cppamqp::exception); | |||
| } | |||
| TEST(AmqpTest, Channel_publish_success) | |||
| { | |||
| connection con; | |||
| con.tcp_connect("localhost", 1234); | |||
| channel channel = con.open_channel(3); | |||
| StrictAmqpMock mock; | |||
| InSequence seq; | |||
| EXPECT_CALL(mock, amqp_basic_publish(AmqpMock::defaultConnectionState, 3, BytesEq("my_exchange"), BytesEq("my_routing_key"), true, false, NotNull(), BytesEq("the_message"))) | |||
| .WillOnce(Return(0)); | |||
| publish_options po; | |||
| EXPECT_NO_THROW(channel.publish("my_exchange", "my_routing_key", publish_flags({ publish_flag::mandatory }), "the_message", &po)); | |||
| } | |||
| TEST(AmqpTest, Channel_consume_consumeFailed) | |||
| { | |||
| connection con; | |||
| con.tcp_connect("localhost", 1234); | |||
| channel channel = con.open_channel(3); | |||
| StrictAmqpMock mock; | |||
| InSequence seq; | |||
| EXPECT_CALL(mock, amqp_basic_consume(AmqpMock::defaultConnectionState, 3, BytesEq("my_queue"), BytesEq("my_consumer_tag"), true, false, false, _)) | |||
| .WillOnce(Return(nullptr)); | |||
| EXPECT_CALL(mock, amqp_get_rpc_reply(AmqpMock::defaultConnectionState)) | |||
| .WillOnce(Return(AmqpMock::defaultRpcReply)); | |||
| EXPECT_THROW(channel.consume("my_queue", "my_consumer_tag", consume_flags(consume_flag::no_local)), cppamqp::exception); | |||
| } | |||
| TEST(AmqpTest, Channel_consume_rpcReplyError) | |||
| { | |||
| connection con; | |||
| con.tcp_connect("localhost", 1234); | |||
| channel channel = con.open_channel(3); | |||
| amqp_basic_consume_ok_t consumeOk { { 7, const_cast<char*>("new_tag") } }; | |||
| StrictAmqpMock mock; | |||
| InSequence seq; | |||
| EXPECT_CALL(mock, amqp_basic_consume(AmqpMock::defaultConnectionState, 3, BytesEq("my_queue"), BytesEq("my_consumer_tag"), false, true, false, _)) | |||
| .WillOnce(Return(&consumeOk)); | |||
| EXPECT_CALL(mock, amqp_get_rpc_reply(AmqpMock::defaultConnectionState)) | |||
| .WillOnce(Return(amqp_rpc_reply_t { | |||
| static_cast<amqp_response_type_enum>(AMQP_RESPONSE_LIBRARY_EXCEPTION), | |||
| { 0, nullptr }, | |||
| 666 })); | |||
| EXPECT_CALL(mock, amqp_error_string2(666)) | |||
| .WillOnce(Return("error from hell >:D")); | |||
| EXPECT_THROW(channel.consume("my_queue", "my_consumer_tag", consume_flags(consume_flag::no_ack)), cppamqp::exception); | |||
| } | |||
| TEST(AmqpTest, Channel_consume_success) | |||
| { | |||
| connection con; | |||
| con.tcp_connect("localhost", 1234); | |||
| channel channel = con.open_channel(3); | |||
| amqp_basic_consume_ok_t consumeOk { { 7, const_cast<char*>("new_tag") } }; | |||
| StrictAmqpMock mock; | |||
| InSequence seq; | |||
| EXPECT_CALL(mock, amqp_basic_consume(AmqpMock::defaultConnectionState, 3, BytesEq("my_queue"), BytesEq("my_consumer_tag"), false, false, true, _)) | |||
| .WillOnce(Return(&consumeOk)); | |||
| EXPECT_CALL(mock, amqp_get_rpc_reply(AmqpMock::defaultConnectionState)) | |||
| .WillOnce(Return(AmqpMock::defaultRpcReply)); | |||
| auto tag = channel.consume("my_queue", "my_consumer_tag", consume_flags(consume_flag::exclusive)); | |||
| EXPECT_EQ(std::string("new_tag"), tag); | |||
| } | |||
| TEST(AmqpTest, Channel_close_failed) | |||
| { | |||
| connection con; | |||
| con.tcp_connect("localhost", 1234); | |||
| channel channel = con.open_channel(3); | |||
| StrictAmqpMock mock; | |||
| InSequence seq; | |||
| EXPECT_CALL(mock, amqp_channel_close(AmqpMock::defaultConnectionState, 3, 500)) | |||
| .WillOnce(Return(amqp_rpc_reply_t { | |||
| static_cast<amqp_response_type_enum>(AMQP_RESPONSE_LIBRARY_EXCEPTION), | |||
| { 0, nullptr }, | |||
| 666 })); | |||
| EXPECT_CALL(mock, amqp_error_string2(666)) | |||
| .WillOnce(Return("error from hell >:D")); | |||
| EXPECT_THROW(channel.close(500), cppamqp::exception); | |||
| } | |||
| TEST(AmqpTest, Channel_close_success) | |||
| { | |||
| connection con; | |||
| con.tcp_connect("localhost", 1234); | |||
| channel channel = con.open_channel(3); | |||
| StrictAmqpMock mock; | |||
| InSequence seq; | |||
| EXPECT_CALL(mock, amqp_channel_close(AmqpMock::defaultConnectionState, 3, 200)) | |||
| .WillOnce(Return(AmqpMock::defaultRpcReply)); | |||
| EXPECT_NO_THROW(channel.close(200)); | |||
| } | |||
| @@ -0,0 +1,120 @@ | |||
| #include <stdarg.h> | |||
| #include "mock.h" | |||
| const amqp_table_t amqp_empty_table = { 0, nullptr }; | |||
| AmqpMock* amqp_mock_instance = nullptr; | |||
| amqp_connection_state_t AmqpMock::defaultConnectionState = reinterpret_cast<amqp_connection_state_t>(1); | |||
| amqp_socket_t* AmqpMock::defaultSocket = reinterpret_cast<amqp_socket_t*>(2); | |||
| amqp_rpc_reply_t AmqpMock::defaultRpcReply { | |||
| static_cast<amqp_response_type_enum>(AMQP_RESPONSE_NORMAL), | |||
| { 0, nullptr }, | |||
| 0 | |||
| }; | |||
| void AmqpMock::setInstance(AmqpMock* value) | |||
| { | |||
| amqp_mock_instance = value; | |||
| } | |||
| void AmqpMock::clearInstance(AmqpMock* value) | |||
| { | |||
| if (amqp_mock_instance == value) | |||
| amqp_mock_instance = nullptr; | |||
| } | |||
| const char* amqp_error_string2(int err) | |||
| { return (amqp_mock_instance ? amqp_mock_instance->amqp_error_string2(err) : nullptr); } | |||
| amqp_channel_open_ok_t* amqp_channel_open(amqp_connection_state_t state, amqp_channel_t channel) | |||
| { return (amqp_mock_instance ? amqp_mock_instance->amqp_channel_open(state, channel) : nullptr); } | |||
| amqp_rpc_reply_t amqp_get_rpc_reply(amqp_connection_state_t state) | |||
| { return (amqp_mock_instance ? amqp_mock_instance->amqp_get_rpc_reply(state) : AmqpMock::defaultRpcReply); } | |||
| amqp_rpc_reply_t amqp_channel_close(amqp_connection_state_t state, amqp_channel_t channel, int code) | |||
| { return (amqp_mock_instance ? amqp_mock_instance->amqp_channel_close(state, channel, code) : amqp_rpc_reply_t { }); } | |||
| amqp_queue_declare_ok_t* amqp_queue_declare(amqp_connection_state_t state, amqp_channel_t channel, amqp_bytes_t queue, amqp_boolean_t passive, amqp_boolean_t durable, amqp_boolean_t exclusive, amqp_boolean_t auto_delete, amqp_table_t arguments) | |||
| { return (amqp_mock_instance ? amqp_mock_instance->amqp_queue_declare(state, channel, queue, passive, durable, exclusive, auto_delete, arguments) : nullptr); } | |||
| void amqp_release_buffers(amqp_connection_state_t state) | |||
| { if (amqp_mock_instance) amqp_mock_instance->amqp_release_buffers(state); } | |||
| amqp_rpc_reply_t amqp_consume_message(amqp_connection_state_t state, amqp_envelope_t *envelope, struct timeval *timeout, int flags) | |||
| { return (amqp_mock_instance ? amqp_mock_instance->amqp_consume_message(state, envelope, timeout, flags) : AmqpMock::defaultRpcReply); } | |||
| int amqp_simple_wait_frame_noblock(amqp_connection_state_t state, amqp_frame_t *decoded_frame, struct timeval *tv) | |||
| { return (amqp_mock_instance ? amqp_mock_instance->amqp_simple_wait_frame_noblock(state, decoded_frame, tv) : 0); } | |||
| amqp_rpc_reply_t amqp_read_message(amqp_connection_state_t state, amqp_channel_t channel, amqp_message_t *message, int flags) | |||
| { return (amqp_mock_instance ? amqp_mock_instance->amqp_read_message(state, channel, message, flags) : AmqpMock::defaultRpcReply); } | |||
| amqp_rpc_reply_t amqp_connection_close(amqp_connection_state_t state, int code) | |||
| { return (amqp_mock_instance ? amqp_mock_instance->amqp_connection_close(state, code) : AmqpMock::defaultRpcReply); } | |||
| void amqp_destroy_envelope(amqp_envelope_t *envelope) | |||
| { if (amqp_mock_instance) amqp_mock_instance->amqp_destroy_envelope(envelope); } | |||
| void amqp_destroy_message(amqp_message_t *message) | |||
| { if (amqp_mock_instance) amqp_mock_instance->amqp_destroy_message(message); } | |||
| int amqp_destroy_connection(amqp_connection_state_t state) | |||
| { return (amqp_mock_instance ? amqp_mock_instance->amqp_destroy_connection(state) : 0); } | |||
| void amqp_maybe_release_buffers(amqp_connection_state_t state) | |||
| { if (amqp_mock_instance) amqp_mock_instance->amqp_maybe_release_buffers(state); } | |||
| amqp_connection_state_t amqp_new_connection(void) | |||
| { return (amqp_mock_instance ? amqp_mock_instance->amqp_new_connection() : AmqpMock::defaultConnectionState); } | |||
| amqp_socket_t* amqp_tcp_socket_new(amqp_connection_state_t state) | |||
| { return (amqp_mock_instance ? amqp_mock_instance->amqp_tcp_socket_new(state) : AmqpMock::defaultSocket); } | |||
| int amqp_socket_open(amqp_socket_t *self, const char *host, int port) | |||
| { return (amqp_mock_instance ? amqp_mock_instance->amqp_socket_open(self, host, port) : 0); } | |||
| #pragma clang diagnostic push | |||
| #pragma clang diagnostic ignored "-Wvarargs" | |||
| amqp_rpc_reply_t amqp_login(amqp_connection_state_t state, char const *vhost, int channel_max, int frame_max, int heartbeat, amqp_sasl_method_enum sasl_method, ...) | |||
| { | |||
| if (!amqp_mock_instance) | |||
| return AmqpMock::defaultRpcReply; | |||
| amqp_rpc_reply_t ret; | |||
| va_list args; | |||
| va_start(args, sasl_method); | |||
| switch (sasl_method) | |||
| { | |||
| case AMQP_SASL_METHOD_PLAIN: | |||
| { | |||
| const char* username = va_arg(args, const char*); | |||
| const char* password = va_arg(args, const char*); | |||
| ret = amqp_mock_instance->amqp_login_plain(state, vhost, channel_max, frame_max, heartbeat, sasl_method, username, password); | |||
| } | |||
| break; | |||
| case AMQP_SASL_METHOD_EXTERNAL: | |||
| { | |||
| const char* token = va_arg(args, const char*); | |||
| ret = amqp_mock_instance->amqp_login_external(state, vhost, channel_max, frame_max, heartbeat, sasl_method, token); | |||
| } | |||
| break; | |||
| default: | |||
| ret = AmqpMock::defaultRpcReply; | |||
| } | |||
| va_end(args); | |||
| return ret; | |||
| } | |||
| #pragma clang diagnostic pop | |||
| amqp_queue_bind_ok_t* amqp_queue_bind(amqp_connection_state_t state, amqp_channel_t channel, amqp_bytes_t queue, amqp_bytes_t exchange, amqp_bytes_t routing_key, amqp_table_t arguments) | |||
| { return (amqp_mock_instance ? amqp_mock_instance->amqp_queue_bind(state, channel, queue, exchange, routing_key, arguments) : nullptr); } | |||
| int amqp_basic_publish(amqp_connection_state_t state, amqp_channel_t channel, amqp_bytes_t exchange, amqp_bytes_t routing_key, amqp_boolean_t mandatory, amqp_boolean_t immediate, struct amqp_basic_properties_t_ const *properties, amqp_bytes_t body) | |||
| { return (amqp_mock_instance ? amqp_mock_instance->amqp_basic_publish(state, channel, exchange, routing_key, mandatory, immediate, properties, body) : 0); } | |||
| amqp_basic_consume_ok_t* amqp_basic_consume(amqp_connection_state_t state, amqp_channel_t channel, amqp_bytes_t queue, amqp_bytes_t consumer_tag, amqp_boolean_t no_local, amqp_boolean_t no_ack, amqp_boolean_t exclusive, amqp_table_t arguments) | |||
| { return (amqp_mock_instance ? amqp_mock_instance->amqp_basic_consume(state, channel, queue, consumer_tag, no_local, no_ack, exclusive, arguments) : nullptr); } | |||
| @@ -0,0 +1,47 @@ | |||
| #pragma once | |||
| #include <amqp.h> | |||
| #include <amqp_tcp_socket.h> | |||
| #include <gmock/gmock.h> | |||
| struct AmqpMock | |||
| { | |||
| private: | |||
| static void setInstance(AmqpMock* value); | |||
| static void clearInstance(AmqpMock* value); | |||
| public: | |||
| MOCK_METHOD1(amqp_error_string2, const char* (int err)); | |||
| MOCK_METHOD2(amqp_channel_open, amqp_channel_open_ok_t* (amqp_connection_state_t state, amqp_channel_t channel)); | |||
| MOCK_METHOD1(amqp_get_rpc_reply, amqp_rpc_reply_t (amqp_connection_state_t state)); | |||
| MOCK_METHOD3(amqp_channel_close, amqp_rpc_reply_t (amqp_connection_state_t state, amqp_channel_t channel, int code)); | |||
| MOCK_METHOD8(amqp_queue_declare, amqp_queue_declare_ok_t* (amqp_connection_state_t state, amqp_channel_t channel, amqp_bytes_t queue, amqp_boolean_t passive, amqp_boolean_t durable, amqp_boolean_t exclusive, amqp_boolean_t auto_delete, amqp_table_t arguments)); | |||
| MOCK_METHOD1(amqp_release_buffers, void (amqp_connection_state_t state)); | |||
| MOCK_METHOD4(amqp_consume_message, amqp_rpc_reply_t (amqp_connection_state_t state, amqp_envelope_t *envelope, struct timeval *timeout, int flags)); | |||
| MOCK_METHOD3(amqp_simple_wait_frame_noblock, int (amqp_connection_state_t state, amqp_frame_t *decoded_frame, struct timeval *tv)); | |||
| MOCK_METHOD4(amqp_read_message, amqp_rpc_reply_t (amqp_connection_state_t state, amqp_channel_t channel, amqp_message_t *message, int flags)); | |||
| MOCK_METHOD2(amqp_connection_close, amqp_rpc_reply_t (amqp_connection_state_t state, int code)); | |||
| MOCK_METHOD1(amqp_destroy_envelope, void (amqp_envelope_t *envelope)); | |||
| MOCK_METHOD1(amqp_destroy_message, void (amqp_message_t *message)); | |||
| MOCK_METHOD1(amqp_destroy_connection, int (amqp_connection_state_t state)); | |||
| MOCK_METHOD1(amqp_maybe_release_buffers, void (amqp_connection_state_t state)); | |||
| MOCK_METHOD0(amqp_new_connection, amqp_connection_state_t (void)); | |||
| MOCK_METHOD1(amqp_tcp_socket_new, amqp_socket_t* (amqp_connection_state_t state)); | |||
| MOCK_METHOD3(amqp_socket_open, int (amqp_socket_t *self, const char *host, int port)); | |||
| MOCK_METHOD8(amqp_login_plain, amqp_rpc_reply_t (amqp_connection_state_t state, char const *vhost, int channel_max, int frame_max, int heartbeat, amqp_sasl_method_enum sasl_method, const char* user, const char* pw)); | |||
| MOCK_METHOD7(amqp_login_external, amqp_rpc_reply_t (amqp_connection_state_t state, char const *vhost, int channel_max, int frame_max, int heartbeat, amqp_sasl_method_enum sasl_method, const char* token)); | |||
| MOCK_METHOD6(amqp_queue_bind, amqp_queue_bind_ok_t* (amqp_connection_state_t state, amqp_channel_t channel, amqp_bytes_t queue, amqp_bytes_t exchange, amqp_bytes_t routing_key, amqp_table_t arguments)); | |||
| MOCK_METHOD8(amqp_basic_publish, int (amqp_connection_state_t state, amqp_channel_t channel, amqp_bytes_t exchange, amqp_bytes_t routing_key, amqp_boolean_t mandatory, amqp_boolean_t immediate, struct amqp_basic_properties_t_ const *properties, amqp_bytes_t body)); | |||
| MOCK_METHOD8(amqp_basic_consume, amqp_basic_consume_ok_t* (amqp_connection_state_t state, amqp_channel_t channel, amqp_bytes_t queue, amqp_bytes_t consumer_tag, amqp_boolean_t no_local, amqp_boolean_t no_ack, amqp_boolean_t exclusive, amqp_table_t arguments)); | |||
| static amqp_connection_state_t defaultConnectionState; | |||
| static amqp_socket_t* defaultSocket; | |||
| static amqp_rpc_reply_t defaultRpcReply; | |||
| AmqpMock() | |||
| { setInstance(this); } | |||
| ~AmqpMock() | |||
| { clearInstance(this); } | |||
| }; | |||