From 86b8ad22dbfcbaca0c3ba08015fef715bd108860 Mon Sep 17 00:00:00 2001 From: Larry Osterman Date: Wed, 26 Apr 2023 16:57:41 -0700 Subject: [PATCH 01/17] Added the ability to serialize AMQP messages --- sdk/core/azure-core-amqp/CMakeLists.txt | 1 + .../azure/core/amqp/models/amqp_header.hpp | 34 +- .../azure/core/amqp/models/amqp_message.hpp | 92 +- .../core/amqp/models/amqp_properties.hpp | 31 +- .../azure/core/amqp/models/amqp_protocol.hpp | 76 ++ .../inc/azure/core/amqp/models/amqp_value.hpp | 154 ++- .../azure/core/amqp/private/session_impl.hpp | 2 +- .../src/models/amqp_header.cpp | 43 + .../src/models/amqp_message.cpp | 956 ++++++++----- .../src/models/amqp_properties.cpp | 68 + .../azure-core-amqp/src/models/amqp_value.cpp | 108 +- .../test/ut/amqp_header_tests.cpp | 206 +++ .../test/ut/amqp_message_tests.cpp | 254 +++- .../test/ut/amqp_properties_tests.cpp | 926 +++++++++++++ .../test/ut/amqp_value_tests.cpp | 1201 ++++++++++++++++- .../test/ut/mock_amqp_server.hpp | 5 +- 16 files changed, 3716 insertions(+), 441 deletions(-) create mode 100644 sdk/core/azure-core-amqp/inc/azure/core/amqp/models/amqp_protocol.hpp diff --git a/sdk/core/azure-core-amqp/CMakeLists.txt b/sdk/core/azure-core-amqp/CMakeLists.txt index caca1f78fe..ca30826970 100644 --- a/sdk/core/azure-core-amqp/CMakeLists.txt +++ b/sdk/core/azure-core-amqp/CMakeLists.txt @@ -51,6 +51,7 @@ set (AZURE_CORE_AMQP_HEADER inc/azure/core/amqp/models/amqp_message.hpp inc/azure/core/amqp/models/amqp_properties.hpp inc/azure/core/amqp/models/amqp_value.hpp + inc/azure/core/amqp/models/amqp_protocol.hpp inc/azure/core/amqp/models/message_source.hpp inc/azure/core/amqp/models/message_target.hpp inc/azure/core/amqp/models/messaging_values.hpp diff --git a/sdk/core/azure-core-amqp/inc/azure/core/amqp/models/amqp_header.hpp b/sdk/core/azure-core-amqp/inc/azure/core/amqp/models/amqp_header.hpp index 1c65bfbe69..0bf71df352 100644 --- a/sdk/core/azure-core-amqp/inc/azure/core/amqp/models/amqp_header.hpp +++ b/sdk/core/azure-core-amqp/inc/azure/core/amqp/models/amqp_header.hpp @@ -10,6 +10,7 @@ #include #include #include +#include struct HEADER_INSTANCE_TAG; @@ -30,6 +31,8 @@ namespace Azure { namespace Core { namespace Amqp { namespace Models { MessageHeader() = default; ~MessageHeader() = default; + bool operator==(MessageHeader const&) const noexcept; + /** @brief True if the message is considered "durable" * * @remarks For more information, see [AMQP @@ -65,21 +68,28 @@ namespace Azure { namespace Core { namespace Amqp { namespace Models { */ std::uint32_t DeliveryCount{0}; + + bool ShouldSerialize() const noexcept; + static size_t GetSerializedSize(MessageHeader const& messageHeader); + static std::vector Serialize(MessageHeader const& messageHeader); + static MessageHeader Deserialize(std::uint8_t const* data, size_t size); }; std::ostream& operator<<(std::ostream&, MessageHeader const&); + }}}} // namespace Azure::Core::Amqp::Models namespace Azure { namespace Core { namespace Amqp { namespace Models { namespace _internal { - /** - * @brief uAMQP interoperability functions to convert a MessageHeader to a uAMQP HEADER_HANDLE and - * back. - * - * @remarks This class should not be used directly. It is used by the uAMQP interoperability - * layer. - */ - class MessageHeaderFactory { - public: - static MessageHeader FromUamqp(UniqueMessageHeaderHandle const& properties); - static UniqueMessageHeaderHandle ToUamqp(MessageHeader const& properties); - }; + /** + * @brief uAMQP interoperability functions to convert a MessageHeader to a uAMQP HEADER_HANDLE + * and back. + * + * @remarks This class should not be used directly. It is used by the uAMQP interoperability + * layer. + */ + struct MessageHeaderFactory + { + static MessageHeader FromUamqp(UniqueMessageHeaderHandle const& properties); + static UniqueMessageHeaderHandle ToUamqp(MessageHeader const& properties); + }; + }}}}} // namespace Azure::Core::Amqp::Models::_internal diff --git a/sdk/core/azure-core-amqp/inc/azure/core/amqp/models/amqp_message.hpp b/sdk/core/azure-core-amqp/inc/azure/core/amqp/models/amqp_message.hpp index bf04e4d2c7..e7d3bf7ee7 100644 --- a/sdk/core/azure-core-amqp/inc/azure/core/amqp/models/amqp_message.hpp +++ b/sdk/core/azure-core-amqp/inc/azure/core/amqp/models/amqp_message.hpp @@ -40,6 +40,8 @@ namespace Azure { namespace Core { namespace Amqp { namespace Models { class AmqpMessageFactory; } + constexpr int AmqpMessageFormatValue = 0; // Specifies the message format for an AMQP message. + class AmqpMessage final { public: /** @brief Construct a new AMQP Message object. */ @@ -60,6 +62,8 @@ namespace Azure { namespace Core { namespace Amqp { namespace Models { /** @brief Move an AMQP message object to another object. @returns A reference to this.*/ AmqpMessage& operator=(AmqpMessage&&) noexcept = default; + bool operator==(AmqpMessage const& other) const noexcept; + AmqpMessage(std::nullptr_t) : m_hasValue{false} {} operator bool() const noexcept { return m_hasValue; } @@ -70,13 +74,6 @@ namespace Azure { namespace Core { namespace Amqp { namespace Models { */ MessageHeader Header; - /** @brief Specifies the 'format' of the message. - * - * For more information, see [AMQP Transfer - * performative](http://docs.oasis-open.org/amqp/core/v1.0/os/amqp-core-transport-v1.0-os.html#type-transfer) - */ - Azure::Nullable MessageFormat; - /** @brief Delivery Annotations for the message. * * For more information, see [AMQP Delivery @@ -123,7 +120,24 @@ namespace Azure { namespace Core { namespace Amqp { namespace Models { */ MessageBodyType BodyType{MessageBodyType::None}; - /** @brief Set the body of the message. + /** @brief Sets the body of the message to a list of sequence sections. + * + * An AMQP Message Body can be one of the following formats: + * + * - One or more binary data sections + * - One or more sequence sections. + * - A single AMQP Value. + * + * This method appends the bodySequence value to the sequence of sections. See [Amqp + * Sequence](http://docs.oasis-open.org/amqp/core/v1.0/os/amqp-core-messaging-v1.0-os.html#type-sequence) + * for more information. + * + * @param bodySequence - the list of AMQP values which make up the body of the message. + * + */ + void SetBody(std::vector const& bodySequence); + + /** @brief Appends a list to the body of the message. * * An AMQP Message Body can be one of the following formats: * @@ -131,12 +145,16 @@ namespace Azure { namespace Core { namespace Amqp { namespace Models { * - One or more sequence sections. * - A single AMQP Value. * - * This method sets the body of the message to a sequence of sections. See [Amqp + * This method appends the bodySequence value to the sequence of sections. See [Amqp * Sequence](http://docs.oasis-open.org/amqp/core/v1.0/os/amqp-core-messaging-v1.0-os.html#type-sequence) * for more information. * * @param bodySequence - the list of AMQP values which make up the body of the message. * + * @remarks This is a convenience method to make it simpler to append a single binary value to + * the message body. + * + * */ void SetBody(AmqpList const& bodySequence); @@ -158,7 +176,7 @@ namespace Azure { namespace Core { namespace Amqp { namespace Models { */ void SetBody(std::vector const& bodyBinarySequence); - /** @brief Set the body of the message. + /** @brief Appends a binary value to the body of the message. * * An AMQP Message Body can be one of the following formats: * @@ -172,7 +190,7 @@ namespace Azure { namespace Core { namespace Amqp { namespace Models { * * @param bodyBinary - a single value binary data. * - * @remarks This is a convenience method to make it simpler to set a single binary value for + * @remarks This is a convenience method to make it simpler to append a single binary value to * the message body. * */ @@ -195,11 +213,11 @@ namespace Azure { namespace Core { namespace Amqp { namespace Models { */ void SetBody(AmqpValue const& bodyValue); - /** @brief Returns an Amqp Sequence message body. + /** @brief Returns a list of Amqp Sequence values. * * @remarks This API will fail if BodyType is not MessageBodyType::Sequence. */ - AmqpList GetBodyAsAmqpList() const; + std::vector GetBodyAsAmqpList() const; /** @brief Returns an Amqp Value message body. * @@ -209,15 +227,33 @@ namespace Azure { namespace Core { namespace Amqp { namespace Models { /** @brief Returns an Amqp Binary message body. * - * @remarks This API will fail if BodyType is not MessageBodyType::Data. + * @remarks This API will fail if BodyType is not MessageBodyType::Binary. */ std::vector GetBodyAsBinary() const; + /** @brief Returns the serialized size of the message. + * + * @remarks This API will fail if BodyType is not set. + */ + static size_t GetSerializedSize(AmqpMessage const& message); + + /** @brief Serialize the message into a buffer. + * + * @remarks This API will fail if BodyType is not set. + */ + static std::vector Serialize(AmqpMessage const& message); + + /** @brief Deserialize the message from a buffer. + * + * @remarks This API will fail if BodyType is not set. + */ + static AmqpMessage Deserialize(std::uint8_t const* buffer, size_t size); + friend class _internal::AmqpMessageFactory; private: std::vector m_binaryDataBody; - AmqpList m_amqpSequenceBody; + std::vector m_amqpSequenceBody; AmqpValue m_amqpValueBody; bool m_hasValue{true}; // By default, an AmqpMessage has a value. }; @@ -226,17 +262,17 @@ namespace Azure { namespace Core { namespace Amqp { namespace Models { }}}} // namespace Azure::Core::Amqp::Models namespace Azure { namespace Core { namespace Amqp { namespace Models { namespace _internal { - /** - * @brief uAMQP interoperability functions to convert a MessageProperties to a uAMQP - * PROPERTIES_HANDLE and back. - * - * @remarks This class should not be used directly. It is used by the uAMQP interoperability - * layer. - */ - class AmqpMessageFactory { - public: - static AmqpMessage FromUamqp(UniqueMessageHandle const& properties); - static AmqpMessage FromUamqp(MESSAGE_INSTANCE_TAG* properties); - static UniqueMessageHandle ToUamqp(AmqpMessage const& properties); - }; + /** + * @brief uAMQP interoperability functions to convert a MessageProperties to a uAMQP + * PROPERTIES_HANDLE and back. + * + * @remarks This class should not be used directly. It is used by the uAMQP interoperability + * layer. + */ + class AmqpMessageFactory { + public: + static AmqpMessage FromUamqp(UniqueMessageHandle const& properties); + static AmqpMessage FromUamqp(MESSAGE_INSTANCE_TAG* properties); + static UniqueMessageHandle ToUamqp(AmqpMessage const& properties); + }; }}}}} // namespace Azure::Core::Amqp::Models::_internal diff --git a/sdk/core/azure-core-amqp/inc/azure/core/amqp/models/amqp_properties.hpp b/sdk/core/azure-core-amqp/inc/azure/core/amqp/models/amqp_properties.hpp index ccf64997ff..72ce629a4c 100644 --- a/sdk/core/azure-core-amqp/inc/azure/core/amqp/models/amqp_properties.hpp +++ b/sdk/core/azure-core-amqp/inc/azure/core/amqp/models/amqp_properties.hpp @@ -40,21 +40,28 @@ namespace Azure { namespace Core { namespace Amqp { namespace Models { Azure::Nullable GroupId; Azure::Nullable GroupSequence; Azure::Nullable ReplyToGroupId; + + bool operator==(MessageProperties const&) const noexcept; + bool ShouldSerialize() const noexcept; + + static size_t GetSerializedSize(MessageProperties const& properties); + static std::vector Serialize(MessageProperties const& properties); + static MessageProperties Deserialize(uint8_t const* data, size_t size); }; std::ostream& operator<<(std::ostream&, MessageProperties const&); }}}} // namespace Azure::Core::Amqp::Models namespace Azure { namespace Core { namespace Amqp { namespace Models { namespace _internal { - /** - * @brief uAMQP interoperability functions to convert a MessageProperties to a uAMQP - * PROPERTIES_HANDLE and back. - * - * @remarks This class should not be used directly. It is used by the uAMQP interoperability - * layer. - */ - class MessagePropertiesFactory { - public: - static MessageProperties FromUamqp(UniquePropertiesHandle const& properties); - static UniquePropertiesHandle ToUamqp(MessageProperties const& properties); - }; + /** + * @brief uAMQP interoperability functions to convert a MessageProperties to a uAMQP + * PROPERTIES_HANDLE and back. + * + * @remarks This class should not be used directly. It is used by the uAMQP interoperability + * layer. + */ + class MessagePropertiesFactory { + public: + static MessageProperties FromUamqp(UniquePropertiesHandle const& properties); + static UniquePropertiesHandle ToUamqp(MessageProperties const& properties); + }; }}}}} // namespace Azure::Core::Amqp::Models::_internal diff --git a/sdk/core/azure-core-amqp/inc/azure/core/amqp/models/amqp_protocol.hpp b/sdk/core/azure-core-amqp/inc/azure/core/amqp/models/amqp_protocol.hpp new file mode 100644 index 0000000000..eb46952b9a --- /dev/null +++ b/sdk/core/azure-core-amqp/inc/azure/core/amqp/models/amqp_protocol.hpp @@ -0,0 +1,76 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// SPDX-License-Identifier: MIT + +// +// This file contains protocol level definitions for the AMQP protocol. + +#pragma once + +#include + +struct AMQPVALUE_DECODER_HANDLE_DATA_TAG; + +template <> struct Azure::Core::_internal::UniqueHandleHelper +{ + static void FreeAmqpDecoder(AMQPVALUE_DECODER_HANDLE_DATA_TAG* obj); + + using type = Azure::Core::_internal:: + BasicUniqueHandle; +}; + +namespace Azure { namespace Core { namespace Amqp { namespace _detail { + using UniqueAmqpDecoderHandle + = Azure::Core::_internal::UniqueHandleHelper::type; + + /** @brief AMQP Descriptor values. Note that the AMQP descriptor is technically a tuple of + * domain+id, the domain for internal-to-amqp is defined to be 0x00000000. + * + * See [AMQP Descriptor + * values](http://docs.oasis-open.org/amqp/core/v1.0/os/amqp-core-types-v1.0-os.html#section-descriptor-values) + * for more information. + */ + enum class AmqpDescriptors : std::int64_t + { + // AMQP Performative descriptors. + Error = 0x1d, + Open = 0x10, + Begin = 0x11, + Attach = 0x12, + Flow = 0x13, + Transfer = 0x14, + Disposition = 0x15, + Detach = 0x16, + End = 0x17, + Close = 0x18, + + // Message Dispositions. + Received = 0x23, + Accepted = 0x24, + Rejected = 0x25, + Released = 0x26, + Modified = 0x27, + + // Terminus related descriptors. + Source = 0x28, + Target = 0x29, + + // AMQP Sasl descriptors. + SaslMechanism = 0x40, + SaslInit = 0x41, + SaslChallenge = 0x42, + SaslResponse = 0x43, + SaslOutcome = 0x44, + + // Message related descriptors. + Header = 0x70, + DeliveryAnnotations = 0x71, + MessageAnnotations = 0x72, + Properties = 0x73, + ApplicationProperties = 0x74, + DataBinary = 0x75, + DataAmqpSequence = 0x76, + DataAmqpValue = 0x77, + Footer = 0x78, + }; + +}}}} // namespace Azure::Core::Amqp::_detail diff --git a/sdk/core/azure-core-amqp/inc/azure/core/amqp/models/amqp_value.hpp b/sdk/core/azure-core-amqp/inc/azure/core/amqp/models/amqp_value.hpp index a2a6f69564..7e0b14629b 100644 --- a/sdk/core/azure-core-amqp/inc/azure/core/amqp/models/amqp_value.hpp +++ b/sdk/core/azure-core-amqp/inc/azure/core/amqp/models/amqp_value.hpp @@ -54,10 +54,10 @@ namespace Azure { namespace Core { namespace Amqp { namespace Models { Invalid, Null, Bool, - UByte, - UShort, - UInt, - ULong, + Ubyte, + Ushort, + Uint, + Ulong, Byte, Short, Int, @@ -335,35 +335,166 @@ namespace Azure { namespace Core { namespace Amqp { namespace Models { * @throws std::runtime_error if the underlying AMQP value is not a boolean. */ operator bool() const; + + /** @brief convert the current AMQP Value to an unsigned 8 bit integer. + * + * @returns the value as an unsigned 8 bit integer. + * + * @throws std::runtime_error if the underlying AMQP value is not an unsigned 8 bit integer. + */ operator std::uint8_t() const; + + /** @brief convert the current AMQP Value to a signed 8 bit integer. + * + * @returns the value as an 8 bit integer. + * + * @throws std::runtime_error if the underlying AMQP value is not a signed 8 bit integer. + */ operator std::int8_t() const; + + /** @brief convert the current AMQP Value to a signed 8 bit integer. Convenience function to + * allow an AmqpValue to be constructed from a 'char' value. + * + * @returns the value as a char. + * + * @throws std::runtime_error if the underlying AMQP value is not a signed 8 bit integer. + */ operator char() const; + + /** @brief convert the current AMQP Value to an unsigned 16 bit integer. + * + * @returns the value as an unsigned 16 bit integer. + * + * @throws std::runtime_error if the underlying AMQP value is not an unsigned 16 bit integer. + */ operator std::uint16_t() const; + + /** @brief convert the current AMQP Value to a signed 16 bit integer. + * + * @returns the value as a signed 16 bit integer. + * + * @throws std::runtime_error if the underlying AMQP value is not a signed 16 bit integer. + */ operator std::int16_t() const; + + /** @brief convert the current AMQP Value to an unsigned 32 bit integer. + * + * @returns the value as an unsigned 32 bit integer. + * + * @throws std::runtime_error if the underlying AMQP value is not an unsigned 32 bit integer. + */ operator std::uint32_t() const; + + /** @brief convert the current AMQP Value to a signed 32 bit integer. + * + * @returns the value as a signed 32 bit integer. + * + * @throws std::runtime_error if the underlying AMQP value is not a signed 32 bit integer. + */ operator std::int32_t() const; + + /** @brief convert the current AMQP Value to an unsigned 64 bit integer. + * + * @returns the value as an unsigned 64 bit integer. + * + * @throws std::runtime_error if the underlying AMQP value is not an unsigned 64 bit integer. + */ operator std::uint64_t() const; + + /** @brief convert the current AMQP Value to a signed 64 bit integer. + * + * @returns the value as a signed 64 bit integer. + * + * @throws std::runtime_error if the underlying AMQP value is not a signed 64 bit integer. + */ operator std::int64_t() const; + + /** @brief convert the current AMQP Value to a 32 bit IEEE 'float' value.. + * + * @returns the value as a float. + * + * @throws std::runtime_error if the underlying AMQP value is not a float. + */ operator float() const; + + /** @brief convert the current AMQP Value to a 64 bit IEEE 'double' value. + * + * @returns the value as a double. + * + * @throws std::runtime_error if the underlying AMQP value is not a double. + */ operator double() const; + + /** @brief convert the current AMQP Value to a 32bit UCS32 value. + * + * @returns the value as a 32 bit character. + * + * @throws std::runtime_error if the underlying AMQP value is not a 32 bit character. + */ operator char32_t() const; + + /** @brief convert the current AMQP Value to a string. + * + * @returns the value as a string. + * + * @throws std::runtime_error if the underlying AMQP value is not a string. + */ explicit operator std::string() const; - operator Uuid() const; - // List Operations. + /** @brief convert the current AMQP Value to a UUID. + * + * @returns the value as an Azure::Core::Uuid value. + * + * @throws std::runtime_error if the underlying AMQP value is not a UUID. + */ + operator Azure::Core::Uuid() const; + + /** @brief convert the current AMQP Value to an AmqpList. + * + * @returns the value as an AmqpList. + * + * @throws std::runtime_error if the underlying AMQP value is not a list. + */ AmqpList AsList() const; - // Map operations. + /** @brief convert the current AMQP Value to an AmqpMap. + * + * @returns the value as an AmqpMap. + * + * @throws std::runtime_error if the underlying AMQP value is not a map. + */ AmqpMap AsMap() const; - // Array operations - note that all array items must be of the same type. + /** @brief convert the current AMQP Value to an AmqpArray. + * + * @returns the value as an AmqpArray. + * + * @throws std::runtime_error if the underlying AMQP value is not an array. + */ AmqpArray AsArray() const; + /** @brief convert the current AMQP Value to an AmqpBinaryData. + * + * @returns the value as an AmqpBinaryData. + * + * @throws std::runtime_error if the underlying AMQP value is not a binary data. + */ AmqpBinaryData AsBinary() const; + /** @brief convert the current AMQP Value to an AmqpTimestamp. + * + * @returns the value as an AmqpTimestamp. + * + * @throws std::runtime_error if the underlying AMQP value is not a timestamp. + */ AmqpTimestamp AsTimestamp() const; - // Symbols + /** @brief convert the current AMQP Value to an AmqpSymbol. + * + * @returns the value as an AmqpSymbol. + * + * @throws std::runtime_error if the underlying AMQP value is not a symbol. + */ AmqpSymbol AsSymbol() const; // Composite values - A composite value is functionally a list with a fixed size. @@ -371,6 +502,10 @@ namespace Azure { namespace Core { namespace Amqp { namespace Models { AmqpDescribed AsDescribed() const; + static std::vector Serialize(AmqpValue const& value); + static size_t GetSerializedSize(AmqpValue const& value); + static AmqpValue Deserialize(uint8_t const* data, size_t size); + protected: UniqueAmqpValueHandle m_value; }; @@ -405,6 +540,7 @@ namespace Azure { namespace Core { namespace Amqp { namespace Models { return m_value.at(pos); } bool operator<(ThisType const& that) const { return m_value < that.m_value; } + bool operator==(ThisType const& that) const { return m_value == that.m_value; } bool empty() const noexcept { return m_value.empty(); } /** diff --git a/sdk/core/azure-core-amqp/inc/azure/core/amqp/private/session_impl.hpp b/sdk/core/azure-core-amqp/inc/azure/core/amqp/private/session_impl.hpp index 35cd9951bb..b528999e5f 100644 --- a/sdk/core/azure-core-amqp/inc/azure/core/amqp/private/session_impl.hpp +++ b/sdk/core/azure-core-amqp/inc/azure/core/amqp/private/session_impl.hpp @@ -66,8 +66,8 @@ namespace Azure { namespace Core { namespace Amqp { namespace _detail { private: SessionImpl(); - UniqueAmqpSession m_session; std::shared_ptr<_detail::ConnectionImpl> m_connectionToPoll; + UniqueAmqpSession m_session; _internal::SessionEvents* m_eventHandler{}; // Common::AsyncOperationQueue> m_newLinkAttachedQueue; diff --git a/sdk/core/azure-core-amqp/src/models/amqp_header.cpp b/sdk/core/azure-core-amqp/src/models/amqp_header.cpp index d66e7bd25c..cead4369dd 100644 --- a/sdk/core/azure-core-amqp/src/models/amqp_header.cpp +++ b/sdk/core/azure-core-amqp/src/models/amqp_header.cpp @@ -2,6 +2,7 @@ // SPDX-Licence-Identifier: MIT #include "azure/core/amqp/models/amqp_header.hpp" +#include "azure/core/amqp/models/amqp_value.hpp" #include @@ -16,6 +17,14 @@ void Azure::Core::_internal::UniqueHandleHelper::FreeAmqpHe } namespace Azure { namespace Core { namespace Amqp { namespace Models { + bool MessageHeader::operator==(MessageHeader const& that) const noexcept + { + return this->DeliveryCount == that.DeliveryCount && this->Durable == that.Durable + && this->IsFirstAcquirer == that.IsFirstAcquirer && this->Priority == that.Priority + && this->TimeToLive.HasValue() == that.TimeToLive.HasValue() + && (this->TimeToLive.HasValue() ? this->TimeToLive.Value() == that.TimeToLive.Value() + : true); + } MessageHeader _internal::MessageHeaderFactory::FromUamqp(UniqueMessageHeaderHandle const& handle) { @@ -108,4 +117,38 @@ namespace Azure { namespace Core { namespace Amqp { namespace Models { os << "}"; return os; } + + bool MessageHeader::ShouldSerialize() const noexcept + { + return Durable || (Priority != 4) || TimeToLive.HasValue() || IsFirstAcquirer + || (DeliveryCount != 0); + } + + size_t MessageHeader::GetSerializedSize(MessageHeader const& properties) + { + auto handle = _internal::MessageHeaderFactory::ToUamqp(properties); + AmqpValue propertiesAsValue{amqpvalue_create_header(handle.get())}; + return AmqpValue::GetSerializedSize(propertiesAsValue); + } + + std::vector MessageHeader::Serialize(MessageHeader const& header) + { + auto handle = _internal::MessageHeaderFactory::ToUamqp(header); + AmqpValue headerAsValue{amqpvalue_create_header(handle.get())}; + return Azure::Core::Amqp::Models::AmqpValue::Serialize(headerAsValue); + } + + MessageHeader MessageHeader::Deserialize(std::uint8_t const* data, size_t size) + { + AmqpValue value{AmqpValue::Deserialize(data, size)}; + HEADER_HANDLE handle; + if (amqpvalue_get_header(value, &handle)) + { + throw std::runtime_error("Could not convert value to AMQP Header."); + } + UniqueMessageHeaderHandle uniqueHandle{handle}; + handle = nullptr; + return _internal::MessageHeaderFactory::FromUamqp(uniqueHandle); + } + }}}} // namespace Azure::Core::Amqp::Models diff --git a/sdk/core/azure-core-amqp/src/models/amqp_message.cpp b/sdk/core/azure-core-amqp/src/models/amqp_message.cpp index acaccd484d..061bd9a48f 100644 --- a/sdk/core/azure-core-amqp/src/models/amqp_message.cpp +++ b/sdk/core/azure-core-amqp/src/models/amqp_message.cpp @@ -3,7 +3,12 @@ #include "azure/core/amqp/models/amqp_message.hpp" #include "azure/core/amqp/models/amqp_header.hpp" +#include "azure/core/amqp/models/amqp_protocol.hpp" #include "azure/core/amqp/models/amqp_value.hpp" + +#include +#include +#include #include #include @@ -14,456 +19,745 @@ namespace Azure { namespace Core { namespace _internal { } }}} // namespace Azure::Core::_internal +using namespace Azure::Core::Amqp::_detail; + namespace Azure { namespace Core { namespace Amqp { namespace Models { - namespace { + namespace { - UniqueMessageHeaderHandle GetHeaderFromMessage(MESSAGE_HANDLE message) - { - if (message != nullptr) + UniqueMessageHeaderHandle GetHeaderFromMessage(MESSAGE_HANDLE message) { - HEADER_HANDLE headerValue; - if (!message_get_header(message, &headerValue)) + if (message != nullptr) { - return Azure::Core::_internal::UniqueHandle(headerValue); + HEADER_HANDLE headerValue; + if (!message_get_header(message, &headerValue)) + { + return Azure::Core::_internal::UniqueHandle(headerValue); + } } + return nullptr; } - return nullptr; - } - UniquePropertiesHandle GetPropertiesFromMessage(MESSAGE_HANDLE const& message) - { - if (message != nullptr) + UniquePropertiesHandle GetPropertiesFromMessage(MESSAGE_HANDLE const& message) { - PROPERTIES_HANDLE propertiesValue; - if (!message_get_properties(message, &propertiesValue)) + if (message != nullptr) { - return Azure::Core::_internal::UniqueHandle(propertiesValue); + PROPERTIES_HANDLE propertiesValue; + if (!message_get_properties(message, &propertiesValue)) + { + return Azure::Core::_internal::UniqueHandle(propertiesValue); + } } + return nullptr; } - return nullptr; - } - } // namespace - - // AMQP ApplicationProperties descriptor (AMQP Specification 1.0 section 3.2.5) - // http://docs.oasis-open.org/amqp/core/v1.0/os/amqp-core-messaging-v1.0-os.html#type-application-properties - // - // ApplicationProperties are defined as being a map from string to simple AMQP value types. - constexpr uint64_t AmqpApplicationPropertiesDescriptor = 116; + } // namespace - AmqpMessage _internal::AmqpMessageFactory::FromUamqp(UniqueMessageHandle const& message) - { - return FromUamqp(message.get()); - } - AmqpMessage _internal::AmqpMessageFactory::FromUamqp(MESSAGE_HANDLE message) - { - if (message == nullptr) - { - return AmqpMessage(nullptr); - } - AmqpMessage rv; - rv.Header = _internal::MessageHeaderFactory::FromUamqp(GetHeaderFromMessage(message)); - rv.Properties - = _internal::MessagePropertiesFactory::FromUamqp(GetPropertiesFromMessage(message)); - uint32_t uint32Value; - - // Copy the Message Format from the source message. It will eventually be transferred to the - // Transfer performative when the message is sent. - if (!message_get_message_format(message, &uint32Value)) + AmqpMessage _internal::AmqpMessageFactory::FromUamqp(UniqueMessageHandle const& message) { - rv.MessageFormat = uint32Value; + return FromUamqp(message.get()); } + AmqpMessage _internal::AmqpMessageFactory::FromUamqp(MESSAGE_HANDLE message) { - AMQP_VALUE annotationsVal; - // message_get_delivery_annotations returns a clone of the message annotations. - if (!message_get_delivery_annotations(message, &annotationsVal) && annotationsVal != nullptr) + if (message == nullptr) { - UniqueAmqpValueHandle deliveryAnnotations(annotationsVal); - auto deliveryMap = AmqpValue{deliveryAnnotations.get()}.AsMap(); - rv.DeliveryAnnotations = deliveryMap; + return AmqpMessage(nullptr); } - } - { - // message_get_message_annotations returns a clone of the message annotations. - AMQP_VALUE annotationVal; - if (!message_get_message_annotations(message, &annotationVal) && annotationVal) + AmqpMessage rv; + rv.Header = _internal::MessageHeaderFactory::FromUamqp(GetHeaderFromMessage(message)); + rv.Properties + = _internal::MessagePropertiesFactory::FromUamqp(GetPropertiesFromMessage(message)); + { - UniqueAmqpValueHandle messageAnnotations(annotationVal); - if (messageAnnotations) + delivery_annotations annotationsVal; + // message_get_delivery_annotations returns a clone of the message annotations. + if (!message_get_delivery_annotations(message, &annotationsVal) + && annotationsVal != nullptr) { - auto messageMap = AmqpValue{messageAnnotations.get()}.AsMap(); - rv.MessageAnnotations = messageMap; + UniqueAmqpValueHandle deliveryAnnotations(annotationsVal); + auto deliveryMap = AmqpValue{deliveryAnnotations.get()}.AsMap(); + rv.DeliveryAnnotations = deliveryMap; } } - } - { - /* - * The ApplicationProperties field in an AMQP message for uAMQP expects that the map value - * is wrapped as a described value. A described value has a ULONG descriptor value and a - * value type. - * - * Making things even more interesting, the ApplicationProperties field in an uAMQP message - * is asymmetric. - * - * The MessageSender class will wrap ApplicationProperties in a described value, so when - * setting application properties, the described value must NOT be present, but when - * decoding an application properties, the GetApplicationProperties method has to be able to - * handle both when the described value is present or not. - */ - AMQP_VALUE properties; - if (!message_get_application_properties(message, &properties) && properties) - { - UniqueAmqpValueHandle describedProperties(properties); - properties = nullptr; - if (describedProperties) + { + // message_get_message_annotations returns a clone of the message annotations. + AMQP_VALUE annotationVal; + if (!message_get_message_annotations(message, &annotationVal) && annotationVal) { - AMQP_VALUE value; - if (amqpvalue_get_type(describedProperties.get()) == AMQP_TYPE_DESCRIBED) + UniqueAmqpValueHandle messageAnnotations(annotationVal); + if (messageAnnotations) { - auto describedType = amqpvalue_get_inplace_descriptor(describedProperties.get()); - uint64_t describedTypeValue; - if (amqpvalue_get_ulong(describedType, &describedTypeValue)) + auto messageMap = AmqpValue{messageAnnotations.get()}.AsMap(); + rv.MessageAnnotations = messageMap; + } + } + } + { + /* + * The ApplicationProperties field in an AMQP message for uAMQP expects that the map value + * is wrapped as a described value. A described value has a ULONG descriptor value and a + * value type. + * + * Making things even more interesting, the ApplicationProperties field in an uAMQP message + * is asymmetric. + * + * The MessageSender class will wrap ApplicationProperties in a described value, so when + * setting application properties, the described value must NOT be present, but when + * decoding an application properties, the GetApplicationProperties method has to be able to + * handle both when the described value is present or not. + */ + AMQP_VALUE properties; + if (!message_get_application_properties(message, &properties) && properties) + { + UniqueAmqpValueHandle describedProperties(properties); + properties = nullptr; + if (describedProperties) + { + AMQP_VALUE value; + if (amqpvalue_get_type(describedProperties.get()) == AMQP_TYPE_DESCRIBED) { - throw std::runtime_error("Could not retrieve application properties described type."); + auto describedType = amqpvalue_get_inplace_descriptor(describedProperties.get()); + uint64_t describedTypeValue; + if (amqpvalue_get_ulong(describedType, &describedTypeValue)) + { + throw std::runtime_error( + "Could not retrieve application properties described type."); + } + if (describedTypeValue + != static_cast( + Azure::Core::Amqp::_detail::AmqpDescriptors::ApplicationProperties)) + { + throw std::runtime_error( + "Application Properties are not the corect described type."); + } + + value = amqpvalue_get_inplace_described_value(describedProperties.get()); } - if (describedTypeValue != AmqpApplicationPropertiesDescriptor) + else { - throw std::runtime_error("Application Properties are not the corect described type."); + value = describedProperties.get(); } - - value = amqpvalue_get_inplace_described_value(describedProperties.get()); - } - else - { - value = describedProperties.get(); - } - if (amqpvalue_get_type(value) != AMQP_TYPE_MAP) - { - throw std::runtime_error("Application Properties must be a map?!"); - } - auto appProperties = AmqpMap(value); - for (auto const& val : appProperties) - { - if (val.first.GetType() != AmqpValueType::String) + if (amqpvalue_get_type(value) != AMQP_TYPE_MAP) { - throw std::runtime_error("Key of Application Properties must be a string."); + throw std::runtime_error("Application Properties must be a map?!"); + } + auto appProperties = AmqpMap(value); + for (auto const& val : appProperties) + { + if (val.first.GetType() != AmqpValueType::String) + { + throw std::runtime_error("Key of Application Properties must be a string."); + } + rv.ApplicationProperties.emplace( + std::make_pair(static_cast(val.first), val.second)); } - rv.ApplicationProperties.emplace( - std::make_pair(static_cast(val.first), val.second)); } } } - } - { - annotations footerVal; - if (!message_get_footer(message, &footerVal) && footerVal) { - UniqueAmqpValueHandle footerAnnotations(footerVal); - footerVal = nullptr; - auto footerMap = AmqpValue{footerAnnotations.get()}.AsMap(); - rv.Footer = footerMap; + annotations footerVal; + if (!message_get_footer(message, &footerVal) && footerVal) + { + UniqueAmqpValueHandle footerAnnotations(footerVal); + footerVal = nullptr; + auto footerMap = AmqpValue{footerAnnotations.get()}.AsMap(); + rv.Footer = footerMap; + } } - } - { - MESSAGE_BODY_TYPE bodyType; - - if (!message_get_body_type(message, &bodyType)) { - switch (bodyType) + MESSAGE_BODY_TYPE bodyType; + + if (!message_get_body_type(message, &bodyType)) { - case MESSAGE_BODY_TYPE_NONE: - rv.BodyType = MessageBodyType::None; - break; - case MESSAGE_BODY_TYPE_DATA: { - size_t dataCount; - if (!message_get_body_amqp_data_count(message, &dataCount)) - { - for (auto i = 0ul; i < dataCount; i += 1) + switch (bodyType) + { + case MESSAGE_BODY_TYPE_NONE: + rv.BodyType = MessageBodyType::None; + break; + case MESSAGE_BODY_TYPE_DATA: { + size_t dataCount; + if (!message_get_body_amqp_data_count(message, &dataCount)) { - BINARY_DATA binaryValue; - if (!message_get_body_amqp_data_in_place(message, i, &binaryValue)) + for (auto i = 0ul; i < dataCount; i += 1) { - rv.m_binaryDataBody.push_back(AmqpBinaryData(std::vector( - binaryValue.bytes, binaryValue.bytes + binaryValue.length))); + BINARY_DATA binaryValue; + if (!message_get_body_amqp_data_in_place(message, i, &binaryValue)) + { + rv.m_binaryDataBody.push_back(AmqpBinaryData(std::vector( + binaryValue.bytes, binaryValue.bytes + binaryValue.length))); + } } } + rv.BodyType = MessageBodyType::Data; } - rv.BodyType = MessageBodyType::Data; - } - break; - case MESSAGE_BODY_TYPE_SEQUENCE: { + break; + case MESSAGE_BODY_TYPE_SEQUENCE: { - size_t sequenceCount; - if (!message_get_body_amqp_sequence_count(message, &sequenceCount)) - { - for (auto i = 0ul; i < sequenceCount; i += 1) + size_t sequenceCount; + if (!message_get_body_amqp_sequence_count(message, &sequenceCount)) { - AMQP_VALUE sequence; - if (!message_get_body_amqp_sequence_in_place(message, i, &sequence)) + for (auto i = 0ul; i < sequenceCount; i += 1) { - rv.m_amqpSequenceBody.push_back(sequence); + AMQP_VALUE sequence; + if (!message_get_body_amqp_sequence_in_place(message, i, &sequence)) + { + rv.m_amqpSequenceBody.push_back(sequence); + } } } + rv.BodyType = MessageBodyType::Sequence; } - rv.BodyType = MessageBodyType::Sequence; + break; + case MESSAGE_BODY_TYPE_VALUE: { + AMQP_VALUE bodyValue; + if (!message_get_body_amqp_value_in_place(message, &bodyValue)) + { + rv.m_amqpValueBody = bodyValue; + } + rv.BodyType = MessageBodyType::Value; + } + break; + case MESSAGE_BODY_TYPE_INVALID: + default: + throw std::runtime_error("Unknown body type."); } + } + } + return rv; + } + + UniqueMessageHandle _internal::AmqpMessageFactory::ToUamqp(AmqpMessage const& message) + { + UniqueMessageHandle rv(message_create()); + + // AMQP 1.0 specifies a message format of 0. + if (message_set_message_format(rv.get(), AmqpMessageFormatValue)) + { + throw std::runtime_error("Could not set destination message format."); + } + + if (message_set_header( + rv.get(), _internal::MessageHeaderFactory::ToUamqp(message.Header).get())) + { + throw std::runtime_error("Could not set message header."); + } + if (message_set_properties( + rv.get(), _internal::MessagePropertiesFactory::ToUamqp(message.Properties).get())) + { + throw std::runtime_error("Could not set message properties."); + } + + if (!message.DeliveryAnnotations.empty()) + { + if (message_set_delivery_annotations( + rv.get(), static_cast(message.DeliveryAnnotations).get())) + { + throw std::runtime_error("Could not set delivery annotations."); + } + } + if (!message.MessageAnnotations.empty()) + { + if (message_set_message_annotations( + rv.get(), static_cast(message.MessageAnnotations).get())) + { + throw std::runtime_error("Could not set message annotations."); + } + } + if (!message.ApplicationProperties.empty()) + { + AmqpMap appProperties; + for (auto const& val : message.ApplicationProperties) + { + if ((val.second.GetType() == AmqpValueType::List) + || (val.second.GetType() == AmqpValueType::Map) + || (val.second.GetType() == AmqpValueType::Composite) + || (val.second.GetType() == AmqpValueType::Described)) + { + throw std::runtime_error( + "Message Application Property values must be simple value types"); + } + appProperties.emplace(val); + } + if (message_set_application_properties( + rv.get(), static_cast(appProperties).get())) + { + throw std::runtime_error("Could not set application properties."); + } + } + if (!message.Footer.empty()) + { + if (message_set_footer(rv.get(), static_cast(message.Footer).get())) + { + throw std::runtime_error("Could not set message annotations."); + } + } + switch (message.BodyType) + { + case MessageBodyType::None: break; - case MESSAGE_BODY_TYPE_VALUE: { - AMQP_VALUE bodyValue; - if (!message_get_body_amqp_value_in_place(message, &bodyValue)) + case MessageBodyType::Data: + for (auto const& binaryVal : message.m_binaryDataBody) + { + BINARY_DATA valueData; + valueData.bytes = binaryVal.data(); + valueData.length = static_cast(binaryVal.size()); + if (message_add_body_amqp_data(rv.get(), valueData)) { - rv.m_amqpValueBody = bodyValue; + throw std::runtime_error("Could not set message body AMQP sequence value."); } - rv.BodyType = MessageBodyType::Value; } break; - case MESSAGE_BODY_TYPE_INVALID: - default: - throw std::runtime_error("Unknown body type."); - } + case MessageBodyType::Sequence: + for (auto const& sequenceVal : message.m_amqpSequenceBody) + { + if (message_add_body_amqp_sequence(rv.get(), AmqpValue(sequenceVal))) + { + throw std::runtime_error("Could not set message body AMQP sequence value."); + } + } + break; + case MessageBodyType::Value: + if (message_set_body_amqp_value(rv.get(), message.m_amqpValueBody)) + { + throw std::runtime_error("Could not set message body AMQP value."); + } + break; + case MessageBodyType::Invalid: // LCOV_EXCL_LINE + default: // LCOV_EXCL_LINE + throw std::runtime_error("Unknown message body type."); // LCOV_EXCL_LINE } + return rv; } - return rv; - } - UniqueMessageHandle _internal::AmqpMessageFactory::ToUamqp(AmqpMessage const& message) - { - UniqueMessageHandle rv(message_create()); + std::vector AmqpMessage::GetBodyAsAmqpList() const + { + if (BodyType != MessageBodyType::Sequence) + { + throw std::runtime_error("Invalid body type, should be MessageBodyType::Sequence."); + } + return m_amqpSequenceBody; + } - if (message_set_header( - rv.get(), _internal::MessageHeaderFactory::ToUamqp(message.Header).get())) + void AmqpMessage::SetBody(AmqpBinaryData const& value) + { + BodyType = MessageBodyType::Data; + m_binaryDataBody.push_back(value); + } + void AmqpMessage::SetBody(std::vector const& value) + { + BodyType = MessageBodyType::Data; + m_binaryDataBody = value; + } + void AmqpMessage::SetBody(AmqpValue const& value) + { + BodyType = MessageBodyType::Value; + m_amqpValueBody = value; + } + void AmqpMessage::SetBody(std::vector const& value) { - throw std::runtime_error("Could not set message header."); + BodyType = MessageBodyType::Sequence; + m_amqpSequenceBody = value; } - if (message_set_properties( - rv.get(), _internal::MessagePropertiesFactory::ToUamqp(message.Properties).get())) + void AmqpMessage::SetBody(AmqpList const& value) { - throw std::runtime_error("Could not set message properties."); + BodyType = MessageBodyType::Sequence; + m_amqpSequenceBody.push_back(value); } - if (message.MessageFormat.HasValue()) + AmqpValue AmqpMessage::GetBodyAsAmqpValue() const { - if (message_set_message_format(rv.get(), message.MessageFormat.Value())) + if (BodyType != MessageBodyType::Value) { - throw std::runtime_error("Could not set destination message format."); + throw std::runtime_error("Invalid body type, should be MessageBodyType::Value."); } + return m_amqpValueBody; } - if (!message.DeliveryAnnotations.empty()) + std::vector AmqpMessage::GetBodyAsBinary() const { - if (message_set_delivery_annotations( - rv.get(), static_cast(message.DeliveryAnnotations).get())) + if (BodyType != MessageBodyType::Data) { - throw std::runtime_error("Could not set delivery annotations."); + throw std::runtime_error("Invalid body type, should be MessageBodyType::Value."); } + return m_binaryDataBody; } - if (!message.MessageAnnotations.empty()) + + bool AmqpMessage::operator==(AmqpMessage const& that) const noexcept { - if (message_set_message_annotations( - rv.get(), static_cast(message.MessageAnnotations).get())) - { - throw std::runtime_error("Could not set message annotations."); - } + return (Header == that.Header) && (DeliveryAnnotations == that.DeliveryAnnotations) + && (MessageAnnotations == that.MessageAnnotations) && (Properties == that.Properties) + && (ApplicationProperties == that.ApplicationProperties) && (Footer == that.Footer) + && (BodyType == that.BodyType) && (m_amqpValueBody == that.m_amqpValueBody) + && (m_amqpSequenceBody == that.m_amqpSequenceBody) + && (m_binaryDataBody == that.m_binaryDataBody); } - if (!message.ApplicationProperties.empty()) + + size_t AmqpMessage::GetSerializedSize(AmqpMessage const& message) { - AmqpMap appProperties; - for (auto const& val : message.ApplicationProperties) + size_t serializedSize{}; + + serializedSize += MessageHeader::GetSerializedSize(message.Header); + serializedSize += AmqpValue::GetSerializedSize(message.MessageAnnotations); + serializedSize += MessageProperties::GetSerializedSize(message.Properties); + + // ApplicationProperties is a map of string to value, we need to convert it to an AmqpMap. { - if ((val.second.GetType() == AmqpValueType::List) - || (val.second.GetType() == AmqpValueType::Map) - || (val.second.GetType() == AmqpValueType::Composite) - || (val.second.GetType() == AmqpValueType::Described)) + AmqpMap appProperties; + for (auto const& val : message.ApplicationProperties) { - throw std::runtime_error( - "Message Application Property values must be simple value types"); + if ((val.second.GetType() == AmqpValueType::List) + || (val.second.GetType() == AmqpValueType::Map) + || (val.second.GetType() == AmqpValueType::Composite) + || (val.second.GetType() == AmqpValueType::Described)) + { + throw std::runtime_error( + "Message Application Property values must be simple value types"); + } + appProperties.emplace(val); } - appProperties.emplace(val); + serializedSize += AmqpValue::GetSerializedSize(appProperties); } - if (message_set_application_properties( - rv.get(), static_cast(appProperties).get())) + + switch (message.BodyType) { - throw std::runtime_error("Could not set application properties."); + default: + case MessageBodyType::Invalid: + throw std::runtime_error("Invalid message body type."); + + case MessageBodyType::Value: + serializedSize += AmqpValue::GetSerializedSize(message.m_amqpValueBody); + break; + case MessageBodyType::Data: + for (auto const& val : message.m_binaryDataBody) + { + serializedSize += AmqpValue::GetSerializedSize(val); + } + break; + case MessageBodyType::Sequence: + for (auto const& val : message.m_amqpSequenceBody) + { + serializedSize += AmqpValue::GetSerializedSize(val); + } + break; } + return serializedSize; } - if (!message.Footer.empty()) + + std::vector AmqpMessage::Serialize(AmqpMessage const& message) { - if (message_set_footer(rv.get(), static_cast(message.Footer).get())) + // size_t serializedSize = AmqpMessage::GetSerializedSize(message); + + std::vector rv; + + // Append the message Header to the serialized message. + if (message.Header.ShouldSerialize()) { - throw std::runtime_error("Could not set message annotations."); + auto serializedHeader = MessageHeader::Serialize(message.Header); + rv.insert(rv.end(), serializedHeader.begin(), serializedHeader.end()); } - } - switch (message.BodyType) - { - case MessageBodyType::None: - break; - case MessageBodyType::Data: - for (auto const& binaryVal : message.m_binaryDataBody) + if (!message.DeliveryAnnotations.empty()) + { + AmqpValue deliveryAnnotations{ + amqpvalue_create_delivery_annotations(AmqpValue{message.DeliveryAnnotations})}; + auto serializedDeliveryAnnotations = AmqpValue::Serialize(deliveryAnnotations); + rv.insert( + rv.end(), serializedDeliveryAnnotations.begin(), serializedDeliveryAnnotations.end()); + } + if (!message.MessageAnnotations.empty()) + { + AmqpValue messageAnnotations{ + amqpvalue_create_message_annotations(AmqpValue{message.MessageAnnotations})}; + auto serializedAnnotations = AmqpValue::Serialize(messageAnnotations); + rv.insert(rv.end(), serializedAnnotations.begin(), serializedAnnotations.end()); + } + + if (message.Properties.ShouldSerialize()) + { + auto serializedMessageProperties = MessageProperties::Serialize(message.Properties); + rv.insert(rv.end(), serializedMessageProperties.begin(), serializedMessageProperties.end()); + } + + if (!message.ApplicationProperties.empty()) + { + AmqpMap appProperties; + for (auto const& val : message.ApplicationProperties) { - BINARY_DATA valueData; - valueData.bytes = binaryVal.data(); - valueData.length = static_cast(binaryVal.size()); - if (message_add_body_amqp_data(rv.get(), valueData)) + if ((val.second.GetType() == AmqpValueType::List) + || (val.second.GetType() == AmqpValueType::Map) + || (val.second.GetType() == AmqpValueType::Composite) + || (val.second.GetType() == AmqpValueType::Described)) { - throw std::runtime_error("Could not set message body AMQP sequence value."); + throw std::runtime_error( + "Message Application Property values must be simple value types"); } + appProperties.emplace(val); + } + AmqpValue propertiesValue{ + amqpvalue_create_application_properties(AmqpValue{appProperties})}; + auto serializedApplicationProperties = AmqpValue::Serialize(propertiesValue); + rv.insert( + rv.end(), + serializedApplicationProperties.begin(), + serializedApplicationProperties.end()); + } + + switch (message.BodyType) + { + default: + case MessageBodyType::Invalid: + throw std::runtime_error("Invalid message body type."); + + case MessageBodyType::Value: { + // The message body element is an AMQP Described type, create one and serialize the + // described body. + AmqpDescribed describedBody( + static_cast(AmqpDescriptors::DataAmqpValue), message.m_amqpValueBody); + auto serializedBodyValue = AmqpValue::Serialize(describedBody); + rv.insert(rv.end(), serializedBodyValue.begin(), serializedBodyValue.end()); } break; - case MessageBodyType::Sequence: - for (auto const& sequenceVal : message.m_amqpSequenceBody) - { - if (message_add_body_amqp_sequence(rv.get(), sequenceVal)) + case MessageBodyType::Data: + for (auto const& val : message.m_binaryDataBody) { - throw std::runtime_error("Could not set message body AMQP sequence value."); + AmqpDescribed describedBody( + static_cast(AmqpDescriptors::DataBinary), val); + auto serializedBodyValue = AmqpValue::Serialize(describedBody); + rv.insert(rv.end(), serializedBodyValue.begin(), serializedBodyValue.end()); + } + break; + case MessageBodyType::Sequence: { + for (auto const& val : message.m_amqpSequenceBody) + { + AmqpDescribed describedBody( + static_cast(AmqpDescriptors::DataAmqpSequence), val); + auto serializedBodyValue = AmqpValue::Serialize(describedBody); + rv.insert(rv.end(), serializedBodyValue.begin(), serializedBodyValue.end()); } } - break; - case MessageBodyType::Value: - if (message_set_body_amqp_value(rv.get(), message.m_amqpValueBody)) + } + if (!message.Footer.empty()) + { + AmqpValue footer{amqpvalue_create_footer(AmqpValue{message.Footer})}; + auto serializedFooter = AmqpValue::Serialize(footer); + rv.insert(rv.end(), serializedFooter.begin(), serializedFooter.end()); + } + + return rv; + } + + // The message fields, in their expected order. + std::vector expectedMessageFields{ + AmqpDescriptors::Header, + AmqpDescriptors::DeliveryAnnotations, + AmqpDescriptors::MessageAnnotations, + AmqpDescriptors::Properties, + AmqpDescriptors::ApplicationProperties}; + + namespace { + class AmqpMessageDeserializer { + public: + AmqpMessageDeserializer() + : m_decoder{amqpvalue_decoder_create(OnAmqpMessageFieldDecodedFn, this)} { - throw std::runtime_error("Could not set message body AMQP value."); } - break; - case MessageBodyType::Invalid: // LCOV_EXCL_LINE - default: // LCOV_EXCL_LINE - throw std::runtime_error("Unknown message body type."); // LCOV_EXCL_LINE - } - return rv; - } - AmqpList AmqpMessage::GetBodyAsAmqpList() const - { - if (BodyType != MessageBodyType::Sequence) - { - throw std::runtime_error("Invalid body type, should be MessageBodyType::Sequence."); - } - return m_amqpSequenceBody; - } + AmqpMessage operator()(std::uint8_t const* data, size_t size) const + { + if (amqpvalue_decode_bytes(m_decoder.get(), data, size)) + { + throw std::runtime_error("Could not decode object"); + } + return m_decodedValue; + } - void AmqpMessage::SetBody(AmqpBinaryData const& value) - { - BodyType = MessageBodyType::Data; - m_binaryDataBody.push_back(value); - } - void AmqpMessage::SetBody(std::vector const& value) - { - BodyType = MessageBodyType::Data; - m_binaryDataBody = value; - } - void AmqpMessage::SetBody(AmqpValue const& value) - { - BodyType = MessageBodyType::Value; - m_amqpValueBody = value; - } - void AmqpMessage::SetBody(AmqpList const& value) - { - BodyType = MessageBodyType::Sequence; - m_amqpSequenceBody = value; - } + private: + UniqueAmqpDecoderHandle m_decoder; + AmqpMessage m_decodedValue; - AmqpValue AmqpMessage::GetBodyAsAmqpValue() const - { - if (BodyType != MessageBodyType::Value) - { - throw std::runtime_error("Invalid body type, should be MessageBodyType::Value."); - } - return m_amqpValueBody; - } - std::vector AmqpMessage::GetBodyAsBinary() const - { - if (BodyType != MessageBodyType::Data) + // Invoked on each descriptor encountered while decrypting the message. + static void OnAmqpMessageFieldDecodedFn(void* context, AMQP_VALUE value) + { + auto deserializer = static_cast(context); + + deserializer->OnAmqpMessageFieldDecoded(value); + } + + void OnAmqpMessageFieldDecoded(AmqpValue value) + { + if (value.GetType() != AmqpValueType::Described) + { + throw std::runtime_error("Decoded message field whose type is NOT described."); + } + AmqpDescribed describedType{value.AsDescribed()}; + if (describedType.GetDescriptor().GetType() != AmqpValueType::Ulong) + { + throw std::runtime_error("Decoded message field MUST be a LONG type."); + } + switch ( + static_cast(static_cast(describedType.GetDescriptor()))) + { + case AmqpDescriptors::Header: { + UniqueMessageHeaderHandle messageHeader; + HEADER_HANDLE h; + if (amqpvalue_get_header(value, &h)) + { + throw std::runtime_error("Could not convert field to header."); + } + messageHeader.reset(h); + h = nullptr; + m_decodedValue.Header = _internal::MessageHeaderFactory::FromUamqp(messageHeader); + break; + } + case AmqpDescriptors::DeliveryAnnotations: + m_decodedValue.DeliveryAnnotations = describedType.GetValue().AsMap(); + break; + case AmqpDescriptors::MessageAnnotations: + m_decodedValue.MessageAnnotations = describedType.GetValue().AsMap(); + break; + case AmqpDescriptors::Properties: { + UniquePropertiesHandle properties; + PROPERTIES_HANDLE h; + if (amqpvalue_get_properties(value, &h)) + { + throw std::runtime_error("Could not convert field to header."); + } + properties.reset(h); + h = nullptr; + m_decodedValue.Properties + = _internal::MessagePropertiesFactory::FromUamqp(properties); + break; + } + case AmqpDescriptors::ApplicationProperties: { + auto propertyMap = describedType.GetValue().AsMap(); + for (auto const& val : propertyMap) + { + if (val.first.GetType() != AmqpValueType::String) + { + throw std::runtime_error("Key of applications properties must be a string."); + } + if ((val.second.GetType() == AmqpValueType::List) + || (val.second.GetType() == AmqpValueType::Map) + || (val.second.GetType() == AmqpValueType::Composite) + || (val.second.GetType() == AmqpValueType::Described)) + { + throw std::runtime_error( + "Message Application Property values must be simple value types"); + } + m_decodedValue.ApplicationProperties.emplace( + static_cast(val.first), val.second); + } + break; + } + case AmqpDescriptors::DataAmqpValue: + m_decodedValue.SetBody(describedType.GetValue()); + break; + case AmqpDescriptors::DataAmqpSequence: + m_decodedValue.SetBody(describedType.GetValue().AsList()); + break; + case AmqpDescriptors::DataBinary: + // Each call to SetBody will append the binary value to the vector of binary bodies. + m_decodedValue.SetBody(describedType.GetValue().AsBinary()); + break; + case AmqpDescriptors::Footer: + m_decodedValue.Footer = describedType.GetValue().AsMap(); + break; + default: + throw std::runtime_error("Unknown message descriptor."); + } + } + }; + } // namespace + + AmqpMessage AmqpMessage::Deserialize(std::uint8_t const* buffer, size_t size) { - throw std::runtime_error("Invalid body type, should be MessageBodyType::Value."); + return AmqpMessageDeserializer{}(buffer, size); } - return m_binaryDataBody; - } - std::ostream& operator<<(std::ostream& os, AmqpMessage const& message) - { - os << "Message: " << std::endl; - os << "Header " << message.Header << std::endl; - os << "Properties: " << message.Properties; - os << "Body: [" << std::endl; - switch (message.BodyType) + std::ostream& operator<<(std::ostream& os, AmqpMessage const& message) { - case MessageBodyType::Invalid: // LCOV_EXCL_LINE - os << "Invalid"; // LCOV_EXCL_LINE - break; // LCOV_EXCL_LINE - case MessageBodyType::None: - os << "None"; - break; - case MessageBodyType::Data: { - os << "AMQP Data: ["; - auto bodyBinary = message.GetBodyAsBinary(); - uint8_t i = 0; - for (auto const& val : bodyBinary) - { - os << "Data: " << val << std::endl; - if (i < bodyBinary.size() - 1) + os << "Message: " << std::endl; + os << "Header " << message.Header << std::endl; + os << "Properties: " << message.Properties; + os << "Body: [" << std::endl; + switch (message.BodyType) + { + case MessageBodyType::Invalid: // LCOV_EXCL_LINE + os << "Invalid"; // LCOV_EXCL_LINE + break; // LCOV_EXCL_LINE + case MessageBodyType::None: + os << "None"; + break; + case MessageBodyType::Data: { + os << "AMQP Data: ["; + auto bodyBinary = message.GetBodyAsBinary(); + uint8_t i = 0; + for (auto const& val : bodyBinary) { - os << ", "; + os << "Data: " << val << std::endl; + if (i < bodyBinary.size() - 1) + { + os << ", "; + } + i += 1; } - i += 1; + os << "]"; } - os << "]"; - } - break; - case MessageBodyType::Sequence: { - os << "AMQP Sequence: ["; - auto bodySequence = message.GetBodyAsAmqpList(); - uint8_t i = 0; - for (auto const& val : bodySequence) - { - os << "Sequence: " << val << std::endl; - if (i < bodySequence.size() - 1) + break; + case MessageBodyType::Sequence: { + os << "AMQP Sequence: ["; + auto bodySequence = message.GetBodyAsAmqpList(); + uint8_t i = 0; + for (auto const& val : bodySequence) { - os << ", "; + os << "Sequence: " << val << std::endl; + if (i < bodySequence.size() - 1) + { + os << ", "; + } + i += 1; } - i += 1; + os << "]"; } - os << "]"; - } - break; - case MessageBodyType::Value: - os << "AmqpValue: " << message.GetBodyAsAmqpValue(); break; - } - os << "]"; + case MessageBodyType::Value: + os << "AmqpValue: " << message.GetBodyAsAmqpValue(); + break; + } + os << "]"; - { - if (!message.ApplicationProperties.empty()) { - os << std::endl << "Application Properties: "; - for (auto const& val : message.ApplicationProperties) + if (!message.ApplicationProperties.empty()) { - os << "{" << val.first << ", " << val.second << "}"; + os << std::endl << "Application Properties: "; + for (auto const& val : message.ApplicationProperties) + { + os << "{" << val.first << ", " << val.second << "}"; + } } } - } - if (!message.DeliveryAnnotations.empty()) - { - os << std::endl << "Delivery Annotations: "; - for (auto const& val : message.DeliveryAnnotations) + if (!message.DeliveryAnnotations.empty()) { - os << "{" << val.first << ", " << val.second << "}"; + os << std::endl << "Delivery Annotations: "; + for (auto const& val : message.DeliveryAnnotations) + { + os << "{" << val.first << ", " << val.second << "}"; + } } - } - if (!message.MessageAnnotations.empty()) - { - os << std::endl << "Message Annotations: "; - for (auto const& val : message.MessageAnnotations) + if (!message.MessageAnnotations.empty()) { - os << "{" << val.first << ", " << val.second << "}"; + os << std::endl << "Message Annotations: "; + for (auto const& val : message.MessageAnnotations) + { + os << "{" << val.first << ", " << val.second << "}"; + } } - } - if (!message.Footer.empty()) - { - os << "Footer: "; - for (auto const& val : message.Footer) + if (!message.Footer.empty()) { - os << "{" << val.first << ", " << val.second << "}"; + os << "Footer: "; + for (auto const& val : message.Footer) + { + os << "{" << val.first << ", " << val.second << "}"; + } } + return os; } - return os; - } }}}} // namespace Azure::Core::Amqp::Models diff --git a/sdk/core/azure-core-amqp/src/models/amqp_properties.cpp b/sdk/core/azure-core-amqp/src/models/amqp_properties.cpp index 78dd8160ce..5e44e38e4e 100644 --- a/sdk/core/azure-core-amqp/src/models/amqp_properties.cpp +++ b/sdk/core/azure-core-amqp/src/models/amqp_properties.cpp @@ -229,6 +229,74 @@ namespace Azure { namespace Core { namespace Amqp { namespace Models { return returnValue; } + namespace { + + template bool CompareNullable(T const& left, T const& right) + { + if (left.HasValue() != right.HasValue()) + { + return false; + } + if (left.HasValue()) + { + return left.Value() == right.Value(); + } + return true; + } + } // namespace + + bool MessageProperties::operator==(MessageProperties const& that) const noexcept + { + return ( + CompareNullable(MessageId, that.MessageId) + && CompareNullable(CorrelationId, that.CorrelationId) + && CompareNullable(UserId, that.UserId) && CompareNullable(To, that.To) + && CompareNullable(Subject, that.Subject) && CompareNullable(ReplyTo, that.ReplyTo) + && CompareNullable(ContentType, that.ContentType) + && CompareNullable(ContentEncoding, that.ContentEncoding) + && CompareNullable(AbsoluteExpiryTime, that.AbsoluteExpiryTime) + && CompareNullable(CreationTime, that.CreationTime) + && CompareNullable(GroupId, that.GroupId) + && CompareNullable(GroupSequence, that.GroupSequence) + && CompareNullable(ReplyToGroupId, that.ReplyToGroupId)); + } + + bool MessageProperties::ShouldSerialize() const noexcept + { + return ( + MessageId.HasValue() || CorrelationId.HasValue() || UserId.HasValue() || To.HasValue() + || Subject.HasValue() || ReplyTo.HasValue() || ContentType.HasValue() + || ContentEncoding.HasValue() || AbsoluteExpiryTime.HasValue() || CreationTime.HasValue() + || GroupId.HasValue() || GroupSequence.HasValue() || ReplyToGroupId.HasValue()); + } + + size_t MessageProperties::GetSerializedSize(MessageProperties const& properties) + { + auto handle = _internal::MessagePropertiesFactory::ToUamqp(properties); + AmqpValue propertiesAsValue{amqpvalue_create_properties(handle.get())}; + return AmqpValue::GetSerializedSize(propertiesAsValue); + } + + std::vector MessageProperties::Serialize(MessageProperties const& properties) + { + auto handle = _internal::MessagePropertiesFactory::ToUamqp(properties); + AmqpValue propertiesAsValue{amqpvalue_create_properties(handle.get())}; + return Azure::Core::Amqp::Models::AmqpValue::Serialize(propertiesAsValue); + } + + MessageProperties MessageProperties::Deserialize(uint8_t const* data, size_t size) + { + AmqpValue value{AmqpValue::Deserialize(data, size)}; + PROPERTIES_HANDLE handle; + if (amqpvalue_get_properties(value, &handle)) + { + throw std::runtime_error("Could not convert value to AMQP Properties."); + } + UniquePropertiesHandle uniqueHandle{handle}; + handle = nullptr; + return _internal::MessagePropertiesFactory::FromUamqp(uniqueHandle); + } + namespace { std::string timeToString(std::chrono::system_clock::time_point t) { diff --git a/sdk/core/azure-core-amqp/src/models/amqp_value.cpp b/sdk/core/azure-core-amqp/src/models/amqp_value.cpp index 2a376bbb20..2a1611e025 100644 --- a/sdk/core/azure-core-amqp/src/models/amqp_value.cpp +++ b/sdk/core/azure-core-amqp/src/models/amqp_value.cpp @@ -3,6 +3,7 @@ #include "azure/core/amqp/models/amqp_value.hpp" #include "azure/core/amqp/models/amqp_properties.hpp" +#include "azure/core/amqp/models/amqp_protocol.hpp" // Note: These blank lines are significant because clang-format orders includes alphabetically, but // there are dependencies in the uAMQP headers which require this ordering. @@ -22,10 +23,16 @@ void Azure::Core::_internal::UniqueHandleHelper::FreeAmqpVa { amqpvalue_destroy(value); } +void Azure::Core::_internal::UniqueHandleHelper::FreeAmqpDecoder( + AMQPVALUE_DECODER_HANDLE value) +{ + amqpvalue_decoder_destroy(value); +} namespace Azure { namespace Core { namespace Amqp { namespace Models { - AmqpValue::~AmqpValue() { m_value.reset(); } + AmqpValue::~AmqpValue() {} + AmqpValue::AmqpValue(bool bool_value) : m_value{amqpvalue_create_boolean(bool_value)} {} AmqpValue::AmqpValue(unsigned char byte_value) : m_value{amqpvalue_create_ubyte(byte_value)} {} AmqpValue::AmqpValue(char value) : m_value{amqpvalue_create_byte(value)} {} @@ -256,13 +263,13 @@ namespace Azure { namespace Core { namespace Amqp { namespace Models { return false; case AmqpValueType::Bool: return static_cast(*this) < static_cast(that); - case AmqpValueType::UByte: + case AmqpValueType::Ubyte: return static_cast(*this) < static_cast(that); - case AmqpValueType::UShort: + case AmqpValueType::Ushort: return static_cast(*this) < static_cast(that); - case AmqpValueType::UInt: + case AmqpValueType::Uint: return static_cast(*this) < static_cast(that); - case AmqpValueType::ULong: + case AmqpValueType::Ulong: return static_cast(*this) < static_cast(that); case AmqpValueType::Byte: return static_cast(*this) < static_cast(that); @@ -328,13 +335,13 @@ namespace Azure { namespace Core { namespace Amqp { namespace Models { case AMQP_TYPE_BOOL: return AmqpValueType::Bool; case AMQP_TYPE_UBYTE: - return AmqpValueType::UByte; + return AmqpValueType::Ubyte; case AMQP_TYPE_USHORT: - return AmqpValueType::UShort; + return AmqpValueType::Ushort; case AMQP_TYPE_UINT: - return AmqpValueType::UInt; + return AmqpValueType::Uint; case AMQP_TYPE_ULONG: - return AmqpValueType::ULong; + return AmqpValueType::Ulong; case AMQP_TYPE_BYTE: return AmqpValueType::Byte; case AMQP_TYPE_SHORT: @@ -383,6 +390,78 @@ namespace Azure { namespace Core { namespace Amqp { namespace Models { return os; } + class AmqpValueDeserializer { + public: + AmqpValueDeserializer() : m_decoder{amqpvalue_decoder_create(OnAmqpValueDecoded, this)} {} + + AmqpValue operator()(std::uint8_t const* data, size_t size) const + { + if (amqpvalue_decode_bytes(m_decoder.get(), data, size)) + { + throw std::runtime_error("Could not decode object"); + } + return m_decodedValue; + } + + private: + Azure::Core::Amqp::_detail::UniqueAmqpDecoderHandle m_decoder; + AmqpValue m_decodedValue; + + static void OnAmqpValueDecoded(void* context, AMQP_VALUE value) + { + auto deserializer = static_cast(context); + deserializer->m_decodedValue = value; + } + }; + + AmqpValue AmqpValue::Deserialize(uint8_t const* data, size_t size) + { + return AmqpValueDeserializer{}(data, size); + } + + class AmqpValueSerializer { + public: + AmqpValueSerializer() = default; + + std::vector operator()(AmqpValue const& value) + { + if (amqpvalue_encode(value, OnAmqpValueEncoded, this)) + { + throw std::runtime_error("Could not decode object"); + } + + return m_encodedValue; + } + + private: + std::vector m_encodedValue; + + // The OnAmqpValueEncoded callback appends the array provided to the existing encoded value, + // extending as needed. + // + // Returns 0 if successful, 1 otherwise. + static int OnAmqpValueEncoded(void* context, unsigned char const* bytes, size_t length) + { + auto serializer = static_cast(context); + serializer->m_encodedValue.insert(serializer->m_encodedValue.end(), bytes, bytes + length); + return 0; + } + }; + + std::vector AmqpValue::Serialize(AmqpValue const& value) + { + return AmqpValueSerializer{}(value); + } + size_t AmqpValue::GetSerializedSize(AmqpValue const& value) + { + size_t encodedSize; + if (amqpvalue_get_encoded_size(value, &encodedSize)) + { + throw std::runtime_error("Could not get encoded size for value."); + } + return encodedSize; + } + AmqpArray::AmqpArray(AMQP_VALUE const value) { if (amqpvalue_get_type(value) != AMQP_TYPE_ARRAY) @@ -434,7 +513,7 @@ namespace Azure { namespace Core { namespace Amqp { namespace Models { { if (amqpvalue_get_type(value) != AMQP_TYPE_MAP) { - throw std::runtime_error("Input AMQP value MUST be an array."); + throw std::runtime_error("Input AMQP value MUST be a map."); } std::uint32_t mapSize; if (amqpvalue_get_map_pair_count(value, &mapSize)) @@ -756,6 +835,14 @@ namespace Azure { namespace Core { namespace Amqp { namespace Models { return os; } + std::ostream& operator<<(std::ostream& os, AmqpList const& value) + { + // Let the AmqpValue specialization handle serialization of the list. + AmqpValue arrayValue(value); + os << arrayValue; + return os; + } + std::ostream& operator<<(std::ostream& os, AmqpMap const& value) { // Let the AmqpValue specialization handle serialization of the map. @@ -775,5 +862,4 @@ namespace Azure { namespace Core { namespace Amqp { namespace Models { { return (m_value == nullptr) || (amqpvalue_get_type(m_value.get()) == AMQP_TYPE_NULL); } - }}}} // namespace Azure::Core::Amqp::Models diff --git a/sdk/core/azure-core-amqp/test/ut/amqp_header_tests.cpp b/sdk/core/azure-core-amqp/test/ut/amqp_header_tests.cpp index 9fe44b198e..7945d5bc04 100644 --- a/sdk/core/azure-core-amqp/test/ut/amqp_header_tests.cpp +++ b/sdk/core/azure-core-amqp/test/ut/amqp_header_tests.cpp @@ -94,3 +94,209 @@ TEST_F(TestHeaders, TestFirstAcquirer) EXPECT_EQ(true, header2.IsFirstAcquirer); GTEST_LOG_(INFO) << header; } + +class HeaderSerialization : public testing::Test { +protected: + void SetUp() override {} + void TearDown() override {} +}; + +TEST_F(HeaderSerialization, SerializeHeaderDurable) +{ + { + std::vector buffer; + MessageHeader header; + header.Durable = true; + buffer = MessageHeader::Serialize(header); + + MessageHeader deserialized = MessageHeader::Deserialize(buffer.data(), buffer.size()); + EXPECT_EQ(header, deserialized); + EXPECT_EQ(0, deserialized.DeliveryCount); + EXPECT_EQ(4, deserialized.Priority); + EXPECT_EQ(true, deserialized.Durable); + EXPECT_EQ(false, deserialized.IsFirstAcquirer); + EXPECT_FALSE(header.TimeToLive.HasValue()); + } + { + std::vector testValue{ + 0x00, // Descriptor follows. + 0x53, // Descriptor is small ulong. + 0x70, // Descriptor is for a message header + // (http://docs.oasis-open.org/amqp/core/v1.0/os/amqp-core-messaging-v1.0-os.html#type-header). + 0xc0, // List + 0x02, // 2 bytes long. + 0x01, // 1 elements. + 0x41, // Boolean True. + }; + + MessageHeader deserialized = MessageHeader::Deserialize(testValue.data(), testValue.size()); + EXPECT_EQ(0, deserialized.DeliveryCount); + EXPECT_EQ(4, deserialized.Priority); // Not 100% sure why 4 is the default value, but... + EXPECT_EQ(true, deserialized.Durable); + EXPECT_EQ(false, deserialized.IsFirstAcquirer); + EXPECT_FALSE(deserialized.TimeToLive.HasValue()); + } +} +TEST_F(HeaderSerialization, SerializeHeaderPriority) +{ + { + std::vector buffer; + MessageHeader header; + header.Priority = 8; + buffer = MessageHeader::Serialize(header); + + MessageHeader deserialized = MessageHeader::Deserialize(buffer.data(), buffer.size()); + EXPECT_EQ(header, deserialized); + EXPECT_EQ(0, deserialized.DeliveryCount); + EXPECT_EQ(8, deserialized.Priority); + EXPECT_EQ(false, deserialized.Durable); + EXPECT_EQ(false, deserialized.IsFirstAcquirer); + EXPECT_FALSE(header.TimeToLive.HasValue()); + } + { + std::vector testValue{ + 0x00, // Descriptor follows. + 0x53, // Descriptor is small ulong. + 0x70, // Descriptor is for a message header + // (http://docs.oasis-open.org/amqp/core/v1.0/os/amqp-core-messaging-v1.0-os.html#type-header). + 0xc0, // List + 0x04, // 4 bytes long. + 0x02, // 2 elements. + 0x40, // First element Nil. + 0x50, // Second element ubyte. + 0x08 // byte value (8). + }; + + MessageHeader deserialized = MessageHeader::Deserialize(testValue.data(), testValue.size()); + EXPECT_EQ(0, deserialized.DeliveryCount); + EXPECT_EQ(8, deserialized.Priority); // Not 100% sure why 4 is the default value, but... + EXPECT_EQ(false, deserialized.Durable); + EXPECT_EQ(false, deserialized.IsFirstAcquirer); + EXPECT_FALSE(deserialized.TimeToLive.HasValue()); + } +} + +TEST_F(HeaderSerialization, SerializeHeaderTtl) +{ + { + std::vector buffer; + MessageHeader header; + header.TimeToLive = std::chrono::milliseconds(12345); + buffer = MessageHeader::Serialize(header); + + MessageHeader deserialized = MessageHeader::Deserialize(buffer.data(), buffer.size()); + EXPECT_EQ(header, deserialized); + EXPECT_EQ(0, deserialized.DeliveryCount); + EXPECT_EQ(4, deserialized.Priority); // Not 100% sure why 4 is the default value, but... + EXPECT_EQ(false, deserialized.Durable); + EXPECT_EQ(false, deserialized.IsFirstAcquirer); + EXPECT_EQ(deserialized.TimeToLive.Value(), std::chrono::milliseconds(12345)); + } + { + std::vector testValue{ + 0x00, // Descriptor follows. + 0x53, // Descriptor is small ulong. + 0x70, // Descriptor is for a message header + // (http://docs.oasis-open.org/amqp/core/v1.0/os/amqp-core-messaging-v1.0-os.html#type-header). + 0xc0, // List + 0x08, // 8 bytes long. + 0x03, // 3 elements. + 0x40, // First element Nil. + 0x40, // Second element Nil. + 0x70, // 4 byte uint. + 0x00, // Uint data byte 1 + 0x00, // Uint data byte 2 + 0x30, // Uint data byte 3 + 0x39 // Big endian encoded 12345. + }; + + MessageHeader deserialized = MessageHeader::Deserialize(testValue.data(), testValue.size()); + EXPECT_EQ(0, deserialized.DeliveryCount); + EXPECT_EQ(4, deserialized.Priority); // Not 100% sure why 4 is the default value, but... + EXPECT_EQ(false, deserialized.Durable); + EXPECT_EQ(false, deserialized.IsFirstAcquirer); + EXPECT_EQ(deserialized.TimeToLive.Value(), std::chrono::milliseconds(12345)); + } +} + +TEST_F(HeaderSerialization, SerializeHeaderFirstAcquirer) +{ + { + std::vector buffer; + MessageHeader header; + header.IsFirstAcquirer = true; + buffer = MessageHeader::Serialize(header); + + MessageHeader deserialized = MessageHeader::Deserialize(buffer.data(), buffer.size()); + EXPECT_EQ(header, deserialized); + EXPECT_EQ(0, deserialized.DeliveryCount); + EXPECT_EQ(4, deserialized.Priority); // Not 100% sure why 4 is the default value, but... + EXPECT_EQ(false, deserialized.Durable); + EXPECT_EQ(true, deserialized.IsFirstAcquirer); + EXPECT_FALSE(header.TimeToLive.HasValue()); + } + { + std::vector testValue{ + 0x00, // Descriptor follows. + 0x53, // Descriptor is small ulong. + 0x70, // Descriptor is for a message header + // (http://docs.oasis-open.org/amqp/core/v1.0/os/amqp-core-messaging-v1.0-os.html#type-header). + 0xc0, // List + 0x09, // 8 bytes long. + 0x04, // 3 elements. + 0x40, // First element Nil. + 0x40, // Second element Nil. + 0x40, // Third element Nil. + 0x41 // Fourth element boolean true. + }; + + MessageHeader deserialized = MessageHeader::Deserialize(testValue.data(), testValue.size()); + EXPECT_EQ(0, deserialized.DeliveryCount); + EXPECT_EQ(4, deserialized.Priority); // Not 100% sure why 4 is the default value, but... + EXPECT_EQ(false, deserialized.Durable); + EXPECT_EQ(true, deserialized.IsFirstAcquirer); + EXPECT_FALSE(deserialized.TimeToLive.HasValue()); + } +} + +TEST_F(HeaderSerialization, SerializeHeaderDeliveryCount) +{ + { + std::vector buffer; + MessageHeader header; + header.DeliveryCount = 157; + buffer = MessageHeader::Serialize(header); + + MessageHeader deserialized = MessageHeader::Deserialize(buffer.data(), buffer.size()); + EXPECT_EQ(header, deserialized); + EXPECT_EQ(157, deserialized.DeliveryCount); + EXPECT_EQ(4, deserialized.Priority); // Not 100% sure why 4 is the default value, but... + EXPECT_EQ(false, deserialized.Durable); + EXPECT_EQ(false, deserialized.IsFirstAcquirer); + EXPECT_FALSE(header.TimeToLive.HasValue()); + } + { + std::vector testValue{ + 0x00, // Descriptor follows. + 0x53, // Descriptor is small ulong. + 0x70, // Descriptor is for a message header + // (http://docs.oasis-open.org/amqp/core/v1.0/os/amqp-core-messaging-v1.0-os.html#type-header). + 0xc0, // List + 0x07, // 7 bytes long. + 0x05, // 5 elements. + 0x40, // First element Nil. + 0x40, // Second element Nil. + 0x40, // 3rd Element nil. + 0x40, // 4th element nil. + 0x52, // 5th element small integer + 0x9d // Small integer value. + }; + + MessageHeader deserialized = MessageHeader::Deserialize(testValue.data(), testValue.size()); + EXPECT_EQ(157, deserialized.DeliveryCount); + EXPECT_EQ(4, deserialized.Priority); // Not 100% sure why 4 is the default value, but... + EXPECT_EQ(false, deserialized.Durable); + EXPECT_EQ(false, deserialized.IsFirstAcquirer); + EXPECT_FALSE(deserialized.TimeToLive.HasValue()); + } +} diff --git a/sdk/core/azure-core-amqp/test/ut/amqp_message_tests.cpp b/sdk/core/azure-core-amqp/test/ut/amqp_message_tests.cpp index 879c780490..9ff9236969 100644 --- a/sdk/core/azure-core-amqp/test/ut/amqp_message_tests.cpp +++ b/sdk/core/azure-core-amqp/test/ut/amqp_message_tests.cpp @@ -119,37 +119,43 @@ TEST_F(TestMessage, TestProperties) GTEST_LOG_(INFO) << message; } -TEST_F(TestMessage, TestFormat) -{ - AmqpMessage message; - message.MessageFormat = 12345; - - auto messageInstance = _internal::AmqpMessageFactory::ToUamqp(message); - AmqpMessage message2(_internal::AmqpMessageFactory::FromUamqp(messageInstance)); - - EXPECT_EQ(message2.MessageFormat.Value(), 12345); - GTEST_LOG_(INFO) << message; -} - TEST_F(TestMessage, TestBodyAmqpSequence) { - AmqpMessage message; + { + AmqpMessage message; - message.SetBody({"Test", 95, AmqpMap{{3, 5}, {4, 9}}}); + message.SetBody({"Test", 95, AmqpMap{{3, 5}, {4, 9}}}); - EXPECT_EQ(3, message.GetBodyAsAmqpList().size()); - EXPECT_EQ("Test", static_cast(message.GetBodyAsAmqpList().at(0))); - EXPECT_EQ(95, static_cast(message.GetBodyAsAmqpList().at(1))); - EXPECT_EQ(message.BodyType, MessageBodyType::Sequence); + EXPECT_EQ(1, message.GetBodyAsAmqpList().size()); + EXPECT_EQ("Test", static_cast(message.GetBodyAsAmqpList()[0].at(0))); + EXPECT_EQ(95, static_cast(message.GetBodyAsAmqpList()[0].at(1))); + EXPECT_EQ(message.BodyType, MessageBodyType::Sequence); - auto messageInstance = _internal::AmqpMessageFactory::ToUamqp(message); - AmqpMessage message2(_internal::AmqpMessageFactory::FromUamqp(messageInstance)); - EXPECT_EQ(3, message2.GetBodyAsAmqpList().size()); - EXPECT_EQ("Test", static_cast(message2.GetBodyAsAmqpList().at(0))); - EXPECT_EQ(95, static_cast(message2.GetBodyAsAmqpList().at(1))); - EXPECT_EQ(message2.BodyType, MessageBodyType::Sequence); + auto messageInstance = _internal::AmqpMessageFactory::ToUamqp(message); + AmqpMessage message2(_internal::AmqpMessageFactory::FromUamqp(messageInstance)); + EXPECT_EQ(1, message2.GetBodyAsAmqpList().size()); + EXPECT_EQ(message, message2); + EXPECT_EQ("Test", static_cast(message2.GetBodyAsAmqpList()[0].at(0))); + EXPECT_EQ(95, static_cast(message2.GetBodyAsAmqpList()[0].at(1))); + EXPECT_EQ(message2.BodyType, MessageBodyType::Sequence); - GTEST_LOG_(INFO) << message; + GTEST_LOG_(INFO) << message; + } + { + AmqpMessage message; + message.SetBody({{1}, {"Test", 3}, {"Test", 95, AmqpMap{{3, 5}, {4, 9}}}}); + EXPECT_EQ(3, message.GetBodyAsAmqpList().size()); + EXPECT_EQ("Test", static_cast(message.GetBodyAsAmqpList()[1].at(0))); + EXPECT_EQ(95, static_cast(message.GetBodyAsAmqpList()[2].at(1))); + EXPECT_EQ(message.BodyType, MessageBodyType::Sequence); + auto messageInstance = _internal::AmqpMessageFactory::ToUamqp(message); + AmqpMessage message2(_internal::AmqpMessageFactory::FromUamqp(messageInstance)); + EXPECT_EQ(3, message2.GetBodyAsAmqpList().size()); + EXPECT_EQ("Test", static_cast(message2.GetBodyAsAmqpList()[2].at(0))); + EXPECT_EQ(95, static_cast(message2.GetBodyAsAmqpList()[2].at(1))); + EXPECT_EQ(message2.BodyType, MessageBodyType::Sequence); + GTEST_LOG_(INFO) << message; + } } TEST_F(TestMessage, TestBodyAmqpData) @@ -177,3 +183,201 @@ TEST_F(TestMessage, TestBodyAmqpData) GTEST_LOG_(INFO) << message; } + +class MessageSerialization : public testing::Test { +protected: + void SetUp() override {} + void TearDown() override {} +}; + +TEST_F(MessageSerialization, SerializeMessageBodyValue) +{ // Body as a single AMQP value. + { + std::vector buffer; + AmqpMessage message; + message.Properties.MessageId = "12345"; + message.SetBody("String Value Body."); + buffer = AmqpMessage::Serialize(message); + + AmqpMessage deserialized = AmqpMessage::Deserialize(buffer.data(), buffer.size()); + EXPECT_EQ(message, deserialized); + } +} +// Body as a single BinaryData value. +TEST_F(MessageSerialization, SerializeMessageBodyBinary) +{ + { + std::vector buffer; + AmqpMessage message; + message.Properties.MessageId = "12345"; + message.SetBody(AmqpBinaryData{'T', 'e', 's', 't', ' ', 'b', 'o', 'd', 'y', 0}); + buffer = AmqpMessage::Serialize(message); + AmqpMessage deserialized = AmqpMessage::Deserialize(buffer.data(), buffer.size()); + EXPECT_EQ(message, deserialized); + } + + // Body as AMQP Value. + { + std::vector buffer; + AmqpMessage message; + message.Properties.MessageId = "12345"; + message.SetBody(AmqpMap{{"key1", "value1"}, {"key2", "value2"}}); + buffer = AmqpMessage::Serialize(message); + AmqpMessage deserialized = AmqpMessage::Deserialize(buffer.data(), buffer.size()); + EXPECT_EQ(message, deserialized); + } + // Body as a vector of BinaryData values. + { + std::vector buffer; + AmqpMessage message; + message.Properties.MessageId = "12345"; + message.SetBody(std::vector{ + AmqpBinaryData{'T', 'e', 's', 't', ' ', 'b', 'o', 'd', 'y', 0}, + AmqpBinaryData{1, 3, 5, 7, 9, 10}}); + buffer = AmqpMessage::Serialize(message); + AmqpMessage deserialized = AmqpMessage::Deserialize(buffer.data(), buffer.size()); + EXPECT_EQ(message, deserialized); + } + // Body as a vector of BinaryData values, v2. + { + std::vector buffer; + AmqpMessage message; + message.Properties.MessageId = "12345"; + message.SetBody(AmqpBinaryData{'T', 'e', 's', 't', ' ', 'b', 'o', 'd', 'y', 0}); + message.SetBody(AmqpBinaryData{1, 3, 5, 7, 9, 10}); + buffer = AmqpMessage::Serialize(message); + AmqpMessage deserialized = AmqpMessage::Deserialize(buffer.data(), buffer.size()); + EXPECT_EQ(deserialized.GetBodyAsBinary().size(), 2); + EXPECT_EQ(message, deserialized); + } +} + +TEST_F(MessageSerialization, SerializeMessageBodySequence) +{ + // Body as a single AMQP Sequence. + { + std::vector buffer; + AmqpMessage message; + message.Properties.MessageId = "12345"; + message.Properties.ContentType = "application/binary"; + message.Footer["footer1"] = "value1"; + message.SetBody(AmqpList{1, 3, 5, 7}); + buffer = AmqpMessage::Serialize(message); + AmqpMessage deserialized = AmqpMessage::Deserialize(buffer.data(), buffer.size()); + EXPECT_EQ(message, deserialized); + } + + // Body as a vector of Amqp List values. + { + std::vector buffer; + AmqpMessage message; + message.Properties.MessageId = "12345"; + message.SetBody(std::vector{ + AmqpList{'T', 'e', 's', 't', ' ', 'b', 'o', 'd', 'y', 0}, AmqpList{1, 3, 5, 7, 9, 10}}); + buffer = AmqpMessage::Serialize(message); + AmqpMessage deserialized = AmqpMessage::Deserialize(buffer.data(), buffer.size()); + EXPECT_EQ(message, deserialized); + } + + // Body as a vector of Amqp List values, added one at a time. + { + std::vector buffer; + AmqpMessage message; + message.Properties.MessageId = "12345"; + message.SetBody(AmqpList{'T', 'e', 's', 't', ' ', 'b', 'o', 'd', 'y', 0}); + message.SetBody(AmqpList{1, 3, 5, 7, 9, 10}); + buffer = AmqpMessage::Serialize(message); + AmqpMessage deserialized = AmqpMessage::Deserialize(buffer.data(), buffer.size()); + EXPECT_EQ(message, deserialized); + } +} + +TEST_F(MessageSerialization, SerializeMessageWithHeader) +{ // Body as a single AMQP value, with message header.. + { + std::vector buffer; + AmqpMessage message; + message.Header.Priority = 5; + message.Properties.MessageId = "12345"; + message.SetBody("String Value Body."); + buffer = AmqpMessage::Serialize(message); + + AmqpMessage deserialized = AmqpMessage::Deserialize(buffer.data(), buffer.size()); + EXPECT_EQ(message, deserialized); + } +} + +TEST_F(MessageSerialization, SerializeMessageWithDeliveryAnnotations) +{ // Body as a single AMQP value, with message header.. + { + std::vector buffer; + AmqpMessage message; + message.Header.Priority = 5; + message.Properties.MessageId = "12345"; + message.DeliveryAnnotations["key1"] = "value1"; + message.DeliveryAnnotations["key2"] = "value2"; + + message.SetBody("String Value Body."); + buffer = AmqpMessage::Serialize(message); + + AmqpMessage deserialized = AmqpMessage::Deserialize(buffer.data(), buffer.size()); + EXPECT_EQ(message, deserialized); + } +} + +TEST_F(MessageSerialization, SerializeMessageWithMessageAnnotations) +{ // Body as a single AMQP value, with message header.. + { + std::vector buffer; + AmqpMessage message; + message.Header.Priority = 5; + message.Properties.MessageId = "12345"; + message.MessageAnnotations["key1"] = "value1"; + message.MessageAnnotations["key2"] = "value2"; + + message.SetBody("String Value Body."); + buffer = AmqpMessage::Serialize(message); + + AmqpMessage deserialized = AmqpMessage::Deserialize(buffer.data(), buffer.size()); + EXPECT_EQ(message, deserialized); + } +} + +TEST_F(MessageSerialization, SerializeMessageWithApplicationProperties) +{ // Body as a single AMQP value, with message header.. + { + std::vector buffer; + AmqpMessage message; + message.Header.Priority = 5; + message.Properties.MessageId = "12345"; + message.Properties.ContentEncoding = "utf-8"; + message.MessageAnnotations["key1"] = "value1"; + message.MessageAnnotations["key2"] = "value2"; + message.ApplicationProperties["key1"] = "value1"; + message.ApplicationProperties["key2"] = 37; + + message.SetBody("String Value Body."); + buffer = AmqpMessage::Serialize(message); + + AmqpMessage deserialized = AmqpMessage::Deserialize(buffer.data(), buffer.size()); + EXPECT_EQ(message, deserialized); + } +} +TEST_F(MessageSerialization, SerializeMessageWithFooter) +{ // Body as a single AMQP value, with message header.. + { + std::vector buffer; + AmqpMessage message; + message.Header.Priority = 5; + message.Properties.MessageId = "12345"; + message.Properties.ContentEncoding = "utf-8"; + message.Footer["footer1"] = "value1"; + message.Footer["footer2"] = 37; + + message.SetBody("String Value Body."); + buffer = AmqpMessage::Serialize(message); + + AmqpMessage deserialized = AmqpMessage::Deserialize(buffer.data(), buffer.size()); + EXPECT_EQ(message, deserialized); + } +} diff --git a/sdk/core/azure-core-amqp/test/ut/amqp_properties_tests.cpp b/sdk/core/azure-core-amqp/test/ut/amqp_properties_tests.cpp index 38526dfbcc..515f7ea9ee 100644 --- a/sdk/core/azure-core-amqp/test/ut/amqp_properties_tests.cpp +++ b/sdk/core/azure-core-amqp/test/ut/amqp_properties_tests.cpp @@ -202,3 +202,929 @@ TEST_F(TestProperties, SetSubject) EXPECT_EQ(properties2.Subject.Value(), subject); GTEST_LOG_(INFO) << properties; } + +class PropertySerialization : public testing::Test { +protected: + void SetUp() override {} + void TearDown() override {} +}; + +TEST_F(PropertySerialization, SerializePropertyMessageId) +{ + { + std::vector buffer; + MessageProperties properties; + properties.MessageId = "MessageId1"; + buffer = MessageProperties::Serialize(properties); + + MessageProperties deserialized = MessageProperties::Deserialize(buffer.data(), buffer.size()); + EXPECT_EQ(properties, deserialized); + EXPECT_EQ(AmqpValue("MessageId1"), deserialized.MessageId.Value()); + EXPECT_FALSE(deserialized.UserId.HasValue()); + EXPECT_FALSE(deserialized.To.HasValue()); + EXPECT_FALSE(deserialized.Subject.HasValue()); + EXPECT_FALSE(deserialized.ReplyTo.HasValue()); + EXPECT_FALSE(deserialized.CorrelationId.HasValue()); + EXPECT_FALSE(deserialized.ContentType.HasValue()); + EXPECT_FALSE(deserialized.ContentEncoding.HasValue()); + EXPECT_FALSE(deserialized.AbsoluteExpiryTime.HasValue()); + EXPECT_FALSE(deserialized.CreationTime.HasValue()); + EXPECT_FALSE(deserialized.GroupId.HasValue()); + EXPECT_FALSE(deserialized.GroupSequence.HasValue()); + EXPECT_FALSE(deserialized.ReplyToGroupId.HasValue()); + } + { + std::vector testValue{ + 0x00, // Descriptor follows. + 0x53, // Descriptor is small ulong. + 0x73, // Descriptor is for a message properties + // (http://docs.oasis-open.org/amqp/core/v1.0/os/amqp-core-messaging-v1.0-os.html#type-properties). + 0xc0, // List + 0x0d, // 2 bytes long. + 0x01, // 1 elements. + 0xa1, // String constructor + 0x0a, // String length. + 'M', + 'e', + 's', + 's', + 'a', + 'g', + 'e', + 'I', + 'd', + '1'}; + + MessageProperties deserialized + = MessageProperties::Deserialize(testValue.data(), testValue.size()); + EXPECT_EQ(AmqpValue("MessageId1"), deserialized.MessageId.Value()); + EXPECT_FALSE(deserialized.UserId.HasValue()); + EXPECT_FALSE(deserialized.To.HasValue()); + EXPECT_FALSE(deserialized.Subject.HasValue()); + EXPECT_FALSE(deserialized.ReplyTo.HasValue()); + EXPECT_FALSE(deserialized.CorrelationId.HasValue()); + EXPECT_FALSE(deserialized.ContentType.HasValue()); + EXPECT_FALSE(deserialized.ContentEncoding.HasValue()); + EXPECT_FALSE(deserialized.AbsoluteExpiryTime.HasValue()); + EXPECT_FALSE(deserialized.CreationTime.HasValue()); + EXPECT_FALSE(deserialized.GroupId.HasValue()); + EXPECT_FALSE(deserialized.GroupSequence.HasValue()); + EXPECT_FALSE(deserialized.ReplyToGroupId.HasValue()); + } +} + +TEST_F(PropertySerialization, SerializePropertyUserId) +{ + { + std::vector buffer; + MessageProperties properties; + properties.UserId = {1, 2, 3, 5, 7, 9}; + buffer = MessageProperties::Serialize(properties); + + MessageProperties deserialized = MessageProperties::Deserialize(buffer.data(), buffer.size()); + EXPECT_EQ(properties, deserialized); + EXPECT_FALSE(deserialized.MessageId.HasValue()); + EXPECT_EQ(deserialized.UserId.Value()[0], 1); + EXPECT_EQ(deserialized.UserId.Value()[5], 9); + EXPECT_FALSE(deserialized.To.HasValue()); + EXPECT_FALSE(deserialized.Subject.HasValue()); + EXPECT_FALSE(deserialized.ReplyTo.HasValue()); + EXPECT_FALSE(deserialized.CorrelationId.HasValue()); + EXPECT_FALSE(deserialized.ContentType.HasValue()); + EXPECT_FALSE(deserialized.ContentEncoding.HasValue()); + EXPECT_FALSE(deserialized.AbsoluteExpiryTime.HasValue()); + EXPECT_FALSE(deserialized.CreationTime.HasValue()); + EXPECT_FALSE(deserialized.GroupId.HasValue()); + EXPECT_FALSE(deserialized.GroupSequence.HasValue()); + EXPECT_FALSE(deserialized.ReplyToGroupId.HasValue()); + } + { + std::vector testValue{ + 0x00, // Descriptor follows. + 0x53, // Descriptor is small ulong. + 0x73, // Descriptor is for a message properties + // (http://docs.oasis-open.org/amqp/core/v1.0/os/amqp-core-messaging-v1.0-os.html#type-properties). + 0xc0, // List + 0x0a, // 10 bytes long. + 0x02, // 2 elements. + 0x40, // NIL (MessageId) + 0xa0, // Binary constructor length. + 0x06, // 6 bytes in the binary data. + 0x01, + 0x02, + 0x03, + 0x05, + 0x07, + 0x09}; + + MessageProperties deserialized + = MessageProperties::Deserialize(testValue.data(), testValue.size()); + EXPECT_FALSE(deserialized.MessageId.HasValue()); + EXPECT_EQ(deserialized.UserId.Value()[0], 1); + EXPECT_EQ(deserialized.UserId.Value()[5], 9); + EXPECT_FALSE(deserialized.To.HasValue()); + EXPECT_FALSE(deserialized.Subject.HasValue()); + EXPECT_FALSE(deserialized.ReplyTo.HasValue()); + EXPECT_FALSE(deserialized.CorrelationId.HasValue()); + EXPECT_FALSE(deserialized.ContentType.HasValue()); + EXPECT_FALSE(deserialized.ContentEncoding.HasValue()); + EXPECT_FALSE(deserialized.AbsoluteExpiryTime.HasValue()); + EXPECT_FALSE(deserialized.CreationTime.HasValue()); + EXPECT_FALSE(deserialized.GroupId.HasValue()); + EXPECT_FALSE(deserialized.GroupSequence.HasValue()); + EXPECT_FALSE(deserialized.ReplyToGroupId.HasValue()); + } +} + +TEST_F(PropertySerialization, SerializePropertyTo) +{ + { + std::vector buffer; + MessageProperties properties; + properties.To = AmqpValue("MessageTo"); + buffer = MessageProperties::Serialize(properties); + + MessageProperties deserialized = MessageProperties::Deserialize(buffer.data(), buffer.size()); + EXPECT_EQ(properties, deserialized); + EXPECT_FALSE(deserialized.MessageId.HasValue()); + EXPECT_FALSE(deserialized.UserId.HasValue()); + // EXPECT_FALSE(deserialized.To.HasValue()); + EXPECT_EQ(AmqpValue("MessageTo"), deserialized.To.Value()); + EXPECT_FALSE(deserialized.Subject.HasValue()); + EXPECT_FALSE(deserialized.ReplyTo.HasValue()); + EXPECT_FALSE(deserialized.CorrelationId.HasValue()); + EXPECT_FALSE(deserialized.ContentType.HasValue()); + EXPECT_FALSE(deserialized.ContentEncoding.HasValue()); + EXPECT_FALSE(deserialized.AbsoluteExpiryTime.HasValue()); + EXPECT_FALSE(deserialized.CreationTime.HasValue()); + EXPECT_FALSE(deserialized.GroupId.HasValue()); + EXPECT_FALSE(deserialized.GroupSequence.HasValue()); + EXPECT_FALSE(deserialized.ReplyToGroupId.HasValue()); + } + { + std::vector testValue{ + 0x00, // Descriptor follows. + 0x53, // Descriptor is small ulong. + 0x73, // Descriptor is for a message properties + // (http://docs.oasis-open.org/amqp/core/v1.0/os/amqp-core-messaging-v1.0-os.html#type-properties). + 0xc0, // List + 0x0e, // 14 bytes long. + 0x03, // 3 elements. + 0x40, // NIL + 0x40, // NIL + 0xa1, // String constructor + 0x09, // String length. + 'M', + 'e', + 's', + 's', + 'a', + 'g', + 'e', + 'T', + 'o'}; + + MessageProperties deserialized + = MessageProperties::Deserialize(testValue.data(), testValue.size()); + EXPECT_FALSE(deserialized.MessageId.HasValue()); + EXPECT_FALSE(deserialized.UserId.HasValue()); + // EXPECT_FALSE(deserialized.To.HasValue()); + EXPECT_EQ(AmqpValue("MessageTo"), deserialized.To.Value()); + EXPECT_FALSE(deserialized.Subject.HasValue()); + EXPECT_FALSE(deserialized.ReplyTo.HasValue()); + EXPECT_FALSE(deserialized.CorrelationId.HasValue()); + EXPECT_FALSE(deserialized.ContentType.HasValue()); + EXPECT_FALSE(deserialized.ContentEncoding.HasValue()); + EXPECT_FALSE(deserialized.AbsoluteExpiryTime.HasValue()); + EXPECT_FALSE(deserialized.CreationTime.HasValue()); + EXPECT_FALSE(deserialized.GroupId.HasValue()); + EXPECT_FALSE(deserialized.GroupSequence.HasValue()); + EXPECT_FALSE(deserialized.ReplyToGroupId.HasValue()); + } +} + +TEST_F(PropertySerialization, SerializePropertySubject) +{ + { + std::vector buffer; + MessageProperties properties; + properties.Subject = "Subject"; + buffer = MessageProperties::Serialize(properties); + + MessageProperties deserialized = MessageProperties::Deserialize(buffer.data(), buffer.size()); + EXPECT_EQ(properties, deserialized); + EXPECT_FALSE(deserialized.MessageId.HasValue()); + EXPECT_FALSE(deserialized.UserId.HasValue()); + EXPECT_FALSE(deserialized.To.HasValue()); + EXPECT_EQ("Subject", deserialized.Subject.Value()); + // EXPECT_FALSE(deserialized.Subject.HasValue()); + EXPECT_FALSE(deserialized.ReplyTo.HasValue()); + EXPECT_FALSE(deserialized.CorrelationId.HasValue()); + EXPECT_FALSE(deserialized.ContentType.HasValue()); + EXPECT_FALSE(deserialized.ContentEncoding.HasValue()); + EXPECT_FALSE(deserialized.AbsoluteExpiryTime.HasValue()); + EXPECT_FALSE(deserialized.CreationTime.HasValue()); + EXPECT_FALSE(deserialized.GroupId.HasValue()); + EXPECT_FALSE(deserialized.GroupSequence.HasValue()); + EXPECT_FALSE(deserialized.ReplyToGroupId.HasValue()); + } + { + std::vector testValue{ + 0x00, // Descriptor follows. + 0x53, // Descriptor is small ulong. + 0x73, // Descriptor is for a message properties + // (http://docs.oasis-open.org/amqp/core/v1.0/os/amqp-core-messaging-v1.0-os.html#type-properties). + 0xc0, // List + 0x0d, // 15 bytes long. + 0x04, // 4 elements. + 0x40, // NIL + 0x40, // NIL + 0x40, // NIL + 0xa1, // String constructor + 0x07, // String length. + 'S', + 'u', + 'b', + 'j', + 'e', + 'c', + 't'}; + + MessageProperties deserialized + = MessageProperties::Deserialize(testValue.data(), testValue.size()); + EXPECT_FALSE(deserialized.MessageId.HasValue()); + EXPECT_FALSE(deserialized.UserId.HasValue()); + EXPECT_FALSE(deserialized.To.HasValue()); + EXPECT_EQ("Subject", deserialized.Subject.Value()); + // EXPECT_FALSE(deserialized.Subject.HasValue()); + EXPECT_FALSE(deserialized.ReplyTo.HasValue()); + EXPECT_FALSE(deserialized.CorrelationId.HasValue()); + EXPECT_FALSE(deserialized.ContentType.HasValue()); + EXPECT_FALSE(deserialized.ContentEncoding.HasValue()); + EXPECT_FALSE(deserialized.AbsoluteExpiryTime.HasValue()); + EXPECT_FALSE(deserialized.CreationTime.HasValue()); + EXPECT_FALSE(deserialized.GroupId.HasValue()); + EXPECT_FALSE(deserialized.GroupSequence.HasValue()); + EXPECT_FALSE(deserialized.ReplyToGroupId.HasValue()); + } +} + +TEST_F(PropertySerialization, SerializePropertyReplyTo) +{ + { + std::vector buffer; + MessageProperties properties; + properties.ReplyTo = AmqpValue("ReplyTo"); + buffer = MessageProperties::Serialize(properties); + + MessageProperties deserialized = MessageProperties::Deserialize(buffer.data(), buffer.size()); + EXPECT_EQ(properties, deserialized); + EXPECT_FALSE(deserialized.MessageId.HasValue()); + EXPECT_FALSE(deserialized.UserId.HasValue()); + EXPECT_FALSE(deserialized.To.HasValue()); + EXPECT_FALSE(deserialized.Subject.HasValue()); + EXPECT_EQ(AmqpValue("ReplyTo"), deserialized.ReplyTo.Value()); + // EXPECT_FALSE(deserialized.ReplyTo.HasValue()); + EXPECT_FALSE(deserialized.CorrelationId.HasValue()); + EXPECT_FALSE(deserialized.ContentType.HasValue()); + EXPECT_FALSE(deserialized.ContentEncoding.HasValue()); + EXPECT_FALSE(deserialized.AbsoluteExpiryTime.HasValue()); + EXPECT_FALSE(deserialized.CreationTime.HasValue()); + EXPECT_FALSE(deserialized.GroupId.HasValue()); + EXPECT_FALSE(deserialized.GroupSequence.HasValue()); + EXPECT_FALSE(deserialized.ReplyToGroupId.HasValue()); + } + { + std::vector testValue{ + 0x00, // Descriptor follows. + 0x53, // Descriptor is small ulong. + 0x73, // Descriptor is for a message properties + // (http://docs.oasis-open.org/amqp/core/v1.0/os/amqp-core-messaging-v1.0-os.html#type-properties). + 0xc0, // List + 0x0d, // 15 bytes long. + 0x05, // 5 elements. + 0x40, // NIL + 0x40, // NIL + 0x40, // NIL + 0x40, // NIL + 0xa1, // String constructor + 0x07, // String length. + 'R', + 'e', + 'p', + 'l', + 'y', + 'T', + 'o'}; + + MessageProperties deserialized + = MessageProperties::Deserialize(testValue.data(), testValue.size()); + EXPECT_FALSE(deserialized.MessageId.HasValue()); + EXPECT_FALSE(deserialized.UserId.HasValue()); + EXPECT_FALSE(deserialized.To.HasValue()); + EXPECT_FALSE(deserialized.Subject.HasValue()); + EXPECT_EQ(AmqpValue("ReplyTo"), deserialized.ReplyTo.Value()); + // EXPECT_FALSE(deserialized.ReplyTo.HasValue()); + EXPECT_FALSE(deserialized.CorrelationId.HasValue()); + EXPECT_FALSE(deserialized.ContentType.HasValue()); + EXPECT_FALSE(deserialized.ContentEncoding.HasValue()); + EXPECT_FALSE(deserialized.AbsoluteExpiryTime.HasValue()); + EXPECT_FALSE(deserialized.CreationTime.HasValue()); + EXPECT_FALSE(deserialized.GroupId.HasValue()); + EXPECT_FALSE(deserialized.GroupSequence.HasValue()); + EXPECT_FALSE(deserialized.ReplyToGroupId.HasValue()); + } +} + +TEST_F(PropertySerialization, SerializePropertyCorrelationId) +{ + { + std::vector buffer; + MessageProperties properties; + properties.CorrelationId = AmqpValue("CorrelationId"); + buffer = MessageProperties::Serialize(properties); + + MessageProperties deserialized = MessageProperties::Deserialize(buffer.data(), buffer.size()); + EXPECT_EQ(properties, deserialized); + EXPECT_FALSE(deserialized.MessageId.HasValue()); + EXPECT_FALSE(deserialized.UserId.HasValue()); + EXPECT_FALSE(deserialized.To.HasValue()); + EXPECT_FALSE(deserialized.Subject.HasValue()); + EXPECT_FALSE(deserialized.ReplyTo.HasValue()); + EXPECT_EQ(AmqpValue("CorrelationId"), deserialized.CorrelationId.Value()); + + // EXPECT_FALSE(deserialized.CorrelationId.HasValue()); + EXPECT_FALSE(deserialized.ContentType.HasValue()); + EXPECT_FALSE(deserialized.ContentEncoding.HasValue()); + EXPECT_FALSE(deserialized.AbsoluteExpiryTime.HasValue()); + EXPECT_FALSE(deserialized.CreationTime.HasValue()); + EXPECT_FALSE(deserialized.GroupId.HasValue()); + EXPECT_FALSE(deserialized.GroupSequence.HasValue()); + EXPECT_FALSE(deserialized.ReplyToGroupId.HasValue()); + } + { + std::vector testValue{ + 0x00, // Descriptor follows. + 0x53, // Descriptor is small ulong. + 0x73, // Descriptor is for a message properties + // (http://docs.oasis-open.org/amqp/core/v1.0/os/amqp-core-messaging-v1.0-os.html#type-properties). + 0xc0, // List + 0x15, // 15 bytes long. + 0x06, // 6 elements. + 0x40, // NIL + 0x40, // NIL + 0x40, // NIL + 0x40, // NIL + 0x40, // NIL + 0xa1, // String constructor + 0x0d, // String length. + 'C', + 'o', + 'r', + 'r', + 'e', + 'l', + 'a', + 't', + 'i', + 'o', + 'n', + 'I', + 'd'}; + + MessageProperties deserialized + = MessageProperties::Deserialize(testValue.data(), testValue.size()); + EXPECT_FALSE(deserialized.MessageId.HasValue()); + EXPECT_FALSE(deserialized.UserId.HasValue()); + EXPECT_FALSE(deserialized.To.HasValue()); + EXPECT_FALSE(deserialized.Subject.HasValue()); + EXPECT_FALSE(deserialized.ReplyTo.HasValue()); + EXPECT_EQ(AmqpValue("CorrelationId"), deserialized.CorrelationId.Value()); + + // EXPECT_FALSE(deserialized.CorrelationId.HasValue()); + EXPECT_FALSE(deserialized.ContentType.HasValue()); + EXPECT_FALSE(deserialized.ContentEncoding.HasValue()); + EXPECT_FALSE(deserialized.AbsoluteExpiryTime.HasValue()); + EXPECT_FALSE(deserialized.CreationTime.HasValue()); + EXPECT_FALSE(deserialized.GroupId.HasValue()); + EXPECT_FALSE(deserialized.GroupSequence.HasValue()); + EXPECT_FALSE(deserialized.ReplyToGroupId.HasValue()); + } +} + +TEST_F(PropertySerialization, SerializePropertyContentType) +{ + { + std::vector buffer; + MessageProperties properties; + properties.ContentType = "Text/Plain"; + buffer = MessageProperties::Serialize(properties); + + MessageProperties deserialized = MessageProperties::Deserialize(buffer.data(), buffer.size()); + EXPECT_EQ(properties, deserialized); + EXPECT_FALSE(deserialized.MessageId.HasValue()); + EXPECT_FALSE(deserialized.UserId.HasValue()); + EXPECT_FALSE(deserialized.To.HasValue()); + EXPECT_FALSE(deserialized.Subject.HasValue()); + EXPECT_FALSE(deserialized.ReplyTo.HasValue()); + EXPECT_FALSE(deserialized.CorrelationId.HasValue()); + // EXPECT_FALSE(deserialized.ContentType.HasValue()); + EXPECT_EQ("Text/Plain", deserialized.ContentType.Value()); + EXPECT_FALSE(deserialized.ContentEncoding.HasValue()); + EXPECT_FALSE(deserialized.AbsoluteExpiryTime.HasValue()); + EXPECT_FALSE(deserialized.CreationTime.HasValue()); + EXPECT_FALSE(deserialized.GroupId.HasValue()); + EXPECT_FALSE(deserialized.GroupSequence.HasValue()); + EXPECT_FALSE(deserialized.ReplyToGroupId.HasValue()); + } + { + std::vector testValue{ + 0x00, // Descriptor follows. + 0x53, // Descriptor is small ulong. + 0x73, // Descriptor is for a message properties + // (http://docs.oasis-open.org/amqp/core/v1.0/os/amqp-core-messaging-v1.0-os.html#type-properties). + 0xc0, // List + 0x13, // 13 bytes long. + 0x07, // 6 elements. + 0x40, // NIL + 0x40, // NIL + 0x40, // NIL + 0x40, // NIL + 0x40, // NIL + 0x40, // NIL + 0xa3, // Symbol constructor + 0x0a, // String length. + 'T', + 'e', + 'x', + 't', + '/', + 'P', + 'l', + 'a', + 'i', + 'n'}; + + MessageProperties deserialized + = MessageProperties::Deserialize(testValue.data(), testValue.size()); + EXPECT_FALSE(deserialized.MessageId.HasValue()); + EXPECT_FALSE(deserialized.UserId.HasValue()); + EXPECT_FALSE(deserialized.To.HasValue()); + EXPECT_FALSE(deserialized.Subject.HasValue()); + EXPECT_FALSE(deserialized.ReplyTo.HasValue()); + EXPECT_FALSE(deserialized.CorrelationId.HasValue()); + // EXPECT_FALSE(deserialized.ContentType.HasValue()); + EXPECT_EQ("Text/Plain", deserialized.ContentType.Value()); + EXPECT_FALSE(deserialized.ContentEncoding.HasValue()); + EXPECT_FALSE(deserialized.AbsoluteExpiryTime.HasValue()); + EXPECT_FALSE(deserialized.CreationTime.HasValue()); + EXPECT_FALSE(deserialized.GroupId.HasValue()); + EXPECT_FALSE(deserialized.GroupSequence.HasValue()); + EXPECT_FALSE(deserialized.ReplyToGroupId.HasValue()); + } +} + +TEST_F(PropertySerialization, SerializePropertyContentEncoding) +{ + { + std::vector buffer; + MessageProperties properties; + properties.ContentEncoding = "Utf-8"; + buffer = MessageProperties::Serialize(properties); + + MessageProperties deserialized = MessageProperties::Deserialize(buffer.data(), buffer.size()); + EXPECT_EQ(properties, deserialized); + EXPECT_FALSE(deserialized.MessageId.HasValue()); + EXPECT_FALSE(deserialized.UserId.HasValue()); + EXPECT_FALSE(deserialized.To.HasValue()); + EXPECT_FALSE(deserialized.Subject.HasValue()); + EXPECT_FALSE(deserialized.ReplyTo.HasValue()); + EXPECT_FALSE(deserialized.CorrelationId.HasValue()); + EXPECT_FALSE(deserialized.ContentType.HasValue()); + // EXPECT_FALSE(deserialized.ContentEncoding.HasValue()); + EXPECT_EQ("Utf-8", deserialized.ContentEncoding.Value()); + EXPECT_FALSE(deserialized.AbsoluteExpiryTime.HasValue()); + EXPECT_FALSE(deserialized.CreationTime.HasValue()); + EXPECT_FALSE(deserialized.GroupId.HasValue()); + EXPECT_FALSE(deserialized.GroupSequence.HasValue()); + EXPECT_FALSE(deserialized.ReplyToGroupId.HasValue()); + } + { + std::vector testValue{ + 0x00, // Descriptor follows. + 0x53, // Descriptor is small ulong. + 0x73, // Descriptor is for a message properties + // (http://docs.oasis-open.org/amqp/core/v1.0/os/amqp-core-messaging-v1.0-os.html#type-properties). + 0xc0, // List + 0x0f, // 15 bytes long. + 0x08, // 8 elements. + 0x40, // NIL + 0x40, // NIL + 0x40, // NIL + 0x40, // NIL + 0x40, // NIL + 0x40, // NIL + 0x40, // NIL + 0xa3, // Symbol constructor + 0x05, // String length. + 'U', + 't', + 'f', + '-', + '8'}; + + MessageProperties deserialized + = MessageProperties::Deserialize(testValue.data(), testValue.size()); + EXPECT_FALSE(deserialized.MessageId.HasValue()); + EXPECT_FALSE(deserialized.UserId.HasValue()); + EXPECT_FALSE(deserialized.To.HasValue()); + EXPECT_FALSE(deserialized.Subject.HasValue()); + EXPECT_FALSE(deserialized.ReplyTo.HasValue()); + EXPECT_FALSE(deserialized.CorrelationId.HasValue()); + EXPECT_FALSE(deserialized.ContentType.HasValue()); + // EXPECT_FALSE(deserialized.ContentEncoding.HasValue()); + EXPECT_EQ("Utf-8", deserialized.ContentEncoding.Value()); + EXPECT_FALSE(deserialized.AbsoluteExpiryTime.HasValue()); + EXPECT_FALSE(deserialized.CreationTime.HasValue()); + EXPECT_FALSE(deserialized.GroupId.HasValue()); + EXPECT_FALSE(deserialized.GroupSequence.HasValue()); + EXPECT_FALSE(deserialized.ReplyToGroupId.HasValue()); + } +} + +TEST_F(PropertySerialization, SerializePropertyAbsoluteExpiryTime) +{ + { + std::vector buffer; + MessageProperties properties; + properties.AbsoluteExpiryTime = std::chrono::system_clock::from_time_t( + std::chrono::duration_cast(std::chrono::milliseconds(12345)).count()); + buffer = MessageProperties::Serialize(properties); + + MessageProperties deserialized = MessageProperties::Deserialize(buffer.data(), buffer.size()); + EXPECT_EQ(properties, deserialized); + EXPECT_FALSE(deserialized.MessageId.HasValue()); + EXPECT_FALSE(deserialized.UserId.HasValue()); + EXPECT_FALSE(deserialized.To.HasValue()); + EXPECT_FALSE(deserialized.Subject.HasValue()); + EXPECT_FALSE(deserialized.ReplyTo.HasValue()); + EXPECT_FALSE(deserialized.CorrelationId.HasValue()); + EXPECT_FALSE(deserialized.ContentType.HasValue()); + EXPECT_FALSE(deserialized.ContentEncoding.HasValue()); + // EXPECT_FALSE(deserialized.AbsoluteExpiryTime.HasValue()); + EXPECT_EQ( + deserialized.AbsoluteExpiryTime.Value(), + std::chrono::system_clock::from_time_t( + std::chrono::duration_cast(std::chrono::milliseconds(12345)) + .count())); + EXPECT_FALSE(deserialized.CreationTime.HasValue()); + EXPECT_FALSE(deserialized.GroupId.HasValue()); + EXPECT_FALSE(deserialized.GroupSequence.HasValue()); + EXPECT_FALSE(deserialized.ReplyToGroupId.HasValue()); + } + { + std::vector testValue{ + 0x00, // Descriptor follows. + 0x53, // Descriptor is small ulong. + 0x73, // Descriptor is for a message properties + // (http://docs.oasis-open.org/amqp/core/v1.0/os/amqp-core-messaging-v1.0-os.html#type-properties). + 0xc0, // List + 0x12, // 0x12 bytes long. + 0x09, // 8 elements. + 0x40, // NIL + 0x40, // NIL + 0x40, // NIL + 0x40, // NIL + 0x40, // NIL + 0x40, // NIL + 0x40, // NIL + 0x40, // NIL + 0x83, // Timestamp constructor + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x2e, + 0xe0}; + + MessageProperties deserialized + = MessageProperties::Deserialize(testValue.data(), testValue.size()); + EXPECT_FALSE(deserialized.MessageId.HasValue()); + EXPECT_FALSE(deserialized.UserId.HasValue()); + EXPECT_FALSE(deserialized.To.HasValue()); + EXPECT_FALSE(deserialized.Subject.HasValue()); + EXPECT_FALSE(deserialized.ReplyTo.HasValue()); + EXPECT_FALSE(deserialized.CorrelationId.HasValue()); + EXPECT_FALSE(deserialized.ContentType.HasValue()); + EXPECT_FALSE(deserialized.ContentEncoding.HasValue()); + // EXPECT_FALSE(deserialized.AbsoluteExpiryTime.HasValue()); + EXPECT_EQ( + deserialized.AbsoluteExpiryTime.Value(), + std::chrono::system_clock::from_time_t( + std::chrono::duration_cast(std::chrono::milliseconds(12345)) + .count())); + EXPECT_FALSE(deserialized.CreationTime.HasValue()); + EXPECT_FALSE(deserialized.GroupId.HasValue()); + EXPECT_FALSE(deserialized.GroupSequence.HasValue()); + EXPECT_FALSE(deserialized.ReplyToGroupId.HasValue()); + } +} + +TEST_F(PropertySerialization, SerializePropertyCreationTime) +{ + { + std::vector buffer; + MessageProperties properties; + properties.CreationTime = std::chrono::system_clock::from_time_t( + std::chrono::duration_cast(std::chrono::milliseconds(12345)).count()); + buffer = MessageProperties::Serialize(properties); + + MessageProperties deserialized = MessageProperties::Deserialize(buffer.data(), buffer.size()); + EXPECT_EQ(properties, deserialized); + EXPECT_FALSE(deserialized.MessageId.HasValue()); + EXPECT_FALSE(deserialized.UserId.HasValue()); + EXPECT_FALSE(deserialized.To.HasValue()); + EXPECT_FALSE(deserialized.Subject.HasValue()); + EXPECT_FALSE(deserialized.ReplyTo.HasValue()); + EXPECT_FALSE(deserialized.CorrelationId.HasValue()); + EXPECT_FALSE(deserialized.ContentType.HasValue()); + EXPECT_FALSE(deserialized.ContentEncoding.HasValue()); + EXPECT_FALSE(deserialized.AbsoluteExpiryTime.HasValue()); + // EXPECT_FALSE(deserialized.CreationTime.HasValue()); + EXPECT_EQ( + deserialized.CreationTime.Value(), + std::chrono::system_clock::from_time_t( + std::chrono::duration_cast(std::chrono::milliseconds(12345)) + .count())); + EXPECT_FALSE(deserialized.GroupId.HasValue()); + EXPECT_FALSE(deserialized.GroupSequence.HasValue()); + EXPECT_FALSE(deserialized.ReplyToGroupId.HasValue()); + } + { + std::vector testValue{ + 0x00, // Descriptor follows. + 0x53, // Descriptor is small ulong. + 0x73, // Descriptor is for a message properties + // (http://docs.oasis-open.org/amqp/core/v1.0/os/amqp-core-messaging-v1.0-os.html#type-properties). + 0xc0, // List + 0x13, // 0x12 bytes long. + 0x0a, // 8 elements. + 0x40, // NIL + 0x40, // NIL + 0x40, // NIL + 0x40, // NIL + 0x40, // NIL + 0x40, // NIL + 0x40, // NIL + 0x40, // NIL + 0x40, // NIL + 0x83, // Timestamp constructor + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x2e, + 0xe0}; + + MessageProperties deserialized + = MessageProperties::Deserialize(testValue.data(), testValue.size()); + EXPECT_FALSE(deserialized.MessageId.HasValue()); + EXPECT_FALSE(deserialized.UserId.HasValue()); + EXPECT_FALSE(deserialized.To.HasValue()); + EXPECT_FALSE(deserialized.Subject.HasValue()); + EXPECT_FALSE(deserialized.ReplyTo.HasValue()); + EXPECT_FALSE(deserialized.CorrelationId.HasValue()); + EXPECT_FALSE(deserialized.ContentType.HasValue()); + EXPECT_FALSE(deserialized.ContentEncoding.HasValue()); + EXPECT_FALSE(deserialized.AbsoluteExpiryTime.HasValue()); + // EXPECT_FALSE(deserialized.CreationTime.HasValue()); + EXPECT_EQ( + deserialized.CreationTime.Value(), + std::chrono::system_clock::from_time_t( + std::chrono::duration_cast(std::chrono::milliseconds(12345)) + .count())); + EXPECT_FALSE(deserialized.GroupId.HasValue()); + EXPECT_FALSE(deserialized.GroupSequence.HasValue()); + EXPECT_FALSE(deserialized.ReplyToGroupId.HasValue()); + } +} + +TEST_F(PropertySerialization, SerializePropertyGroupdId) +{ + { + std::vector buffer; + MessageProperties properties; + properties.GroupId = "GroupId"; + buffer = MessageProperties::Serialize(properties); + + MessageProperties deserialized = MessageProperties::Deserialize(buffer.data(), buffer.size()); + EXPECT_EQ(properties, deserialized); + EXPECT_FALSE(deserialized.MessageId.HasValue()); + EXPECT_FALSE(deserialized.UserId.HasValue()); + EXPECT_FALSE(deserialized.To.HasValue()); + EXPECT_FALSE(deserialized.Subject.HasValue()); + EXPECT_FALSE(deserialized.ReplyTo.HasValue()); + EXPECT_FALSE(deserialized.CorrelationId.HasValue()); + EXPECT_FALSE(deserialized.ContentType.HasValue()); + EXPECT_FALSE(deserialized.ContentEncoding.HasValue()); + EXPECT_FALSE(deserialized.AbsoluteExpiryTime.HasValue()); + EXPECT_FALSE(deserialized.CreationTime.HasValue()); + // EXPECT_FALSE(deserialized.GroupId.HasValue()); + EXPECT_EQ("GroupId", deserialized.GroupId.Value()); + EXPECT_FALSE(deserialized.GroupSequence.HasValue()); + EXPECT_FALSE(deserialized.ReplyToGroupId.HasValue()); + } + { + std::vector testValue{ + 0x00, // Descriptor follows. + 0x53, // Descriptor is small ulong. + 0x73, // Descriptor is for a message properties + // (http://docs.oasis-open.org/amqp/core/v1.0/os/amqp-core-messaging-v1.0-os.html#type-properties). + 0xc0, // List + 0x14, // 0x14 bytes long. + 0x0b, // 11 elements. + 0x40, // NIL + 0x40, // NIL + 0x40, // NIL + 0x40, // NIL + 0x40, // NIL + 0x40, // NIL + 0x40, // NIL + 0x40, // NIL + 0x40, // NIL + 0x40, // NIL + 0xa1, // String constructor + 0x07, + 'G', + 'r', + 'o', + 'u', + 'p', + 'I', + 'd'}; + + MessageProperties deserialized + = MessageProperties::Deserialize(testValue.data(), testValue.size()); + EXPECT_FALSE(deserialized.MessageId.HasValue()); + EXPECT_FALSE(deserialized.UserId.HasValue()); + EXPECT_FALSE(deserialized.To.HasValue()); + EXPECT_FALSE(deserialized.Subject.HasValue()); + EXPECT_FALSE(deserialized.ReplyTo.HasValue()); + EXPECT_FALSE(deserialized.CorrelationId.HasValue()); + EXPECT_FALSE(deserialized.ContentType.HasValue()); + EXPECT_FALSE(deserialized.ContentEncoding.HasValue()); + EXPECT_FALSE(deserialized.AbsoluteExpiryTime.HasValue()); + EXPECT_FALSE(deserialized.CreationTime.HasValue()); + // EXPECT_FALSE(deserialized.GroupId.HasValue()); + EXPECT_EQ("GroupId", deserialized.GroupId.Value()); + EXPECT_FALSE(deserialized.GroupSequence.HasValue()); + EXPECT_FALSE(deserialized.ReplyToGroupId.HasValue()); + } +} + +TEST_F(PropertySerialization, SerializePropertyGroupSequence) +{ + { + std::vector buffer; + MessageProperties properties; + properties.GroupSequence = 32767; + buffer = MessageProperties::Serialize(properties); + + MessageProperties deserialized = MessageProperties::Deserialize(buffer.data(), buffer.size()); + EXPECT_EQ(properties, deserialized); + EXPECT_FALSE(deserialized.MessageId.HasValue()); + EXPECT_FALSE(deserialized.UserId.HasValue()); + EXPECT_FALSE(deserialized.To.HasValue()); + EXPECT_FALSE(deserialized.Subject.HasValue()); + EXPECT_FALSE(deserialized.ReplyTo.HasValue()); + EXPECT_FALSE(deserialized.CorrelationId.HasValue()); + EXPECT_FALSE(deserialized.ContentType.HasValue()); + EXPECT_FALSE(deserialized.ContentEncoding.HasValue()); + EXPECT_FALSE(deserialized.AbsoluteExpiryTime.HasValue()); + EXPECT_FALSE(deserialized.CreationTime.HasValue()); + EXPECT_FALSE(deserialized.GroupId.HasValue()); + // EXPECT_FALSE(deserialized.GroupSequence.HasValue()); + EXPECT_EQ(32767, deserialized.GroupSequence.Value()); + EXPECT_FALSE(deserialized.ReplyToGroupId.HasValue()); + } + { + std::vector testValue{ + 0x00, // Descriptor follows. + 0x53, // Descriptor is small ulong. + 0x73, // Descriptor is for a message properties + // (http://docs.oasis-open.org/amqp/core/v1.0/os/amqp-core-messaging-v1.0-os.html#type-properties). + 0xc0, // List + 0x11, // 0x14 bytes long. + 0x0c, // 12 elements. + 0x40, // NIL + 0x40, // NIL + 0x40, // NIL + 0x40, // NIL + 0x40, // NIL + 0x40, // NIL + 0x40, // NIL + 0x40, // NIL + 0x40, // NIL + 0x40, // NIL + 0x40, // NIL + 0x70, // int constructor + 0x00, + 0x00, + 0x7f, + 0xff}; + + MessageProperties deserialized + = MessageProperties::Deserialize(testValue.data(), testValue.size()); + EXPECT_FALSE(deserialized.MessageId.HasValue()); + EXPECT_FALSE(deserialized.UserId.HasValue()); + EXPECT_FALSE(deserialized.To.HasValue()); + EXPECT_FALSE(deserialized.Subject.HasValue()); + EXPECT_FALSE(deserialized.ReplyTo.HasValue()); + EXPECT_FALSE(deserialized.CorrelationId.HasValue()); + EXPECT_FALSE(deserialized.ContentType.HasValue()); + EXPECT_FALSE(deserialized.ContentEncoding.HasValue()); + EXPECT_FALSE(deserialized.AbsoluteExpiryTime.HasValue()); + EXPECT_FALSE(deserialized.CreationTime.HasValue()); + EXPECT_FALSE(deserialized.GroupId.HasValue()); + // EXPECT_FALSE(deserialized.GroupSequence.HasValue()); + EXPECT_EQ(32767, deserialized.GroupSequence.Value()); + EXPECT_FALSE(deserialized.ReplyToGroupId.HasValue()); + } +} + +TEST_F(PropertySerialization, SerializePropertyReplyToGroupId) +{ + { + std::vector buffer; + MessageProperties properties; + properties.ReplyToGroupId = "32767"; + buffer = MessageProperties::Serialize(properties); + + MessageProperties deserialized = MessageProperties::Deserialize(buffer.data(), buffer.size()); + EXPECT_EQ(properties, deserialized); + EXPECT_FALSE(deserialized.MessageId.HasValue()); + EXPECT_FALSE(deserialized.UserId.HasValue()); + EXPECT_FALSE(deserialized.To.HasValue()); + EXPECT_FALSE(deserialized.Subject.HasValue()); + EXPECT_FALSE(deserialized.ReplyTo.HasValue()); + EXPECT_FALSE(deserialized.CorrelationId.HasValue()); + EXPECT_FALSE(deserialized.ContentType.HasValue()); + EXPECT_FALSE(deserialized.ContentEncoding.HasValue()); + EXPECT_FALSE(deserialized.AbsoluteExpiryTime.HasValue()); + EXPECT_FALSE(deserialized.CreationTime.HasValue()); + EXPECT_FALSE(deserialized.GroupId.HasValue()); + EXPECT_FALSE(deserialized.GroupSequence.HasValue()); + // EXPECT_FALSE(deserialized.ReplyToGroupId.HasValue()); + EXPECT_EQ("32767", deserialized.ReplyToGroupId.Value()); + } + { + std::vector testValue{ + 0x00, // Descriptor follows. + 0x53, // Descriptor is small ulong. + 0x73, // Descriptor is for a message properties + // (http://docs.oasis-open.org/amqp/core/v1.0/os/amqp-core-messaging-v1.0-os.html#type-properties). + 0xc0, // List + 0x12, // 0x14 bytes long. + 0x0d, // 13 elements. + 0x40, // NIL + 0x40, // NIL + 0x40, // NIL + 0x40, // NIL + 0x40, // NIL + 0x40, // NIL + 0x40, // NIL + 0x40, // NIL + 0x40, // NIL + 0x40, // NIL + 0x40, // NIL + 0x40, // NIL + 0xa1, // string constructor + 0x05, + '3', + '2', + '7', + '6', + '7'}; + + MessageProperties deserialized + = MessageProperties::Deserialize(testValue.data(), testValue.size()); + EXPECT_FALSE(deserialized.MessageId.HasValue()); + EXPECT_FALSE(deserialized.UserId.HasValue()); + EXPECT_FALSE(deserialized.To.HasValue()); + EXPECT_FALSE(deserialized.Subject.HasValue()); + EXPECT_FALSE(deserialized.ReplyTo.HasValue()); + EXPECT_FALSE(deserialized.CorrelationId.HasValue()); + EXPECT_FALSE(deserialized.ContentType.HasValue()); + EXPECT_FALSE(deserialized.ContentEncoding.HasValue()); + EXPECT_FALSE(deserialized.AbsoluteExpiryTime.HasValue()); + EXPECT_FALSE(deserialized.CreationTime.HasValue()); + EXPECT_FALSE(deserialized.GroupId.HasValue()); + EXPECT_FALSE(deserialized.GroupSequence.HasValue()); + // EXPECT_FALSE(deserialized.ReplyToGroupId.HasValue()); + EXPECT_EQ("32767", deserialized.ReplyToGroupId.Value()); + } +} diff --git a/sdk/core/azure-core-amqp/test/ut/amqp_value_tests.cpp b/sdk/core/azure-core-amqp/test/ut/amqp_value_tests.cpp index a6d56444d5..b360d7ab43 100644 --- a/sdk/core/azure-core-amqp/test/ut/amqp_value_tests.cpp +++ b/sdk/core/azure-core-amqp/test/ut/amqp_value_tests.cpp @@ -5,6 +5,7 @@ #include "azure/core/amqp/models/amqp_value.hpp" #include +#include using namespace Azure::Core::Amqp::Models; @@ -38,7 +39,7 @@ TEST_F(TestValues, SimpleCreate) { AmqpValue value{static_cast(255)}; - EXPECT_EQ(AmqpValueType::UByte, value.GetType()); + EXPECT_EQ(AmqpValueType::Ubyte, value.GetType()); EXPECT_EQ(255, static_cast(value)); EXPECT_TRUE(AmqpValue() < value); } @@ -54,7 +55,7 @@ TEST_F(TestValues, SimpleCreate) { AmqpValue value{static_cast(65535)}; - EXPECT_EQ(AmqpValueType::UShort, value.GetType()); + EXPECT_EQ(AmqpValueType::Ushort, value.GetType()); EXPECT_EQ(65535, static_cast(value)); EXPECT_TRUE(AmqpValue() < value); } @@ -73,7 +74,7 @@ TEST_F(TestValues, SimpleCreate) } { AmqpValue value(32u); - EXPECT_EQ(AmqpValueType::UInt, value.GetType()); + EXPECT_EQ(AmqpValueType::Uint, value.GetType()); EXPECT_EQ(32u, static_cast(value)); EXPECT_TRUE(AmqpValue() < value); } @@ -86,7 +87,7 @@ TEST_F(TestValues, SimpleCreate) } { AmqpValue value(static_cast(39ull)); - EXPECT_EQ(AmqpValueType::ULong, value.GetType()); + EXPECT_EQ(AmqpValueType::Ulong, value.GetType()); EXPECT_EQ(39ull, static_cast(value)); EXPECT_TRUE(AmqpValue() < value); } @@ -237,8 +238,8 @@ TEST_F(TestValues, TestArray) EXPECT_FALSE(array1 < array2); } { - // Because EXPECT_ANY_THROW is a macro, the commas in the lambda below confuse the preprocessor. - // So explicitly capture the lambda and then execute it in the EXPECT_ANY_THROW. + // Because EXPECT_ANY_THROW is a macro, the commas in the lambda below confuse the + // preprocessor. So explicitly capture the lambda and then execute it in the EXPECT_ANY_THROW. auto v = []() { AmqpArray testArray{3.1, 2.9, 14}; }; EXPECT_ANY_THROW(v()); } @@ -317,8 +318,8 @@ TEST_F(TestValues, TestCompositeValue) EXPECT_EQ(compositeVal.size(), testVal.size()); EXPECT_EQ(compositeVal.GetDescriptor(), testVal.GetDescriptor()); - EXPECT_EQ(compositeVal.at(0), testVal.at(0)); - EXPECT_EQ(compositeVal.at(1), testVal.at(1)); + EXPECT_EQ(compositeVal[0], testVal[0]); + EXPECT_EQ(compositeVal[1], testVal[1]); EXPECT_EQ(25, static_cast(testVal.at(0))); EXPECT_EQ(25.0f, static_cast(testVal.at(1))); EXPECT_FALSE(compositeVal < testVal); @@ -360,3 +361,1187 @@ TEST_F(TestValues, TestDescribed) EXPECT_EQ(AmqpValue(described2.GetDescriptor()), AmqpValue(value.GetDescriptor())); } } + +class TestValueSerialization : public testing::Test { +protected: + void SetUp() override {} + void TearDown() override {} +}; + +// AMQP values are serialized as described in the AMQP spec. + +// Test deserializing a null value (0x40) - section 1.6.1. +TEST_F(TestValueSerialization, SerializeNull) +{ + std::vector testVector{0x40}; + AmqpValue value{AmqpValue::Deserialize(testVector.data(), testVector.size())}; + EXPECT_EQ(value.GetType(), AmqpValueType::Null); + + auto val = AmqpValue::Serialize(value); + EXPECT_EQ(1, val.size()); + EXPECT_EQ(0x40, val[0]); +} + +// Test deserializing a boolean value (0x56/0x00 or 0x56/0x01) - section 1.6.2. +TEST_F(TestValueSerialization, SerializeBoolean) +{ + // There are two possible encodings for Boolean values: 0x56 followed by a byte with the value + // 0x00 for false, or 0x56 followed by a byte with the value 0x01 for true. + // The other possible encoding for Boolean values is 0x41 for true and 0x42 for false. + { + std::vector testVector{0x56, 0x01}; + AmqpValue value{AmqpValue::Deserialize(testVector.data(), testVector.size())}; + EXPECT_EQ(value.GetType(), AmqpValueType::Bool); + EXPECT_TRUE(value); + + auto val = AmqpValue::Serialize(value); + EXPECT_EQ(1, val.size()); + EXPECT_EQ(0x41, val[0]); + } + { + std::vector testVector{0x56, 0x00}; + AmqpValue value{AmqpValue::Deserialize(testVector.data(), testVector.size())}; + EXPECT_EQ(value.GetType(), AmqpValueType::Bool); + EXPECT_FALSE(value); + + auto val = AmqpValue::Serialize(value); + EXPECT_EQ(1, val.size()); + EXPECT_EQ(0x42, val[0]); + } + + { + std::vector testVector{0x41}; + AmqpValue value{AmqpValue::Deserialize(testVector.data(), testVector.size())}; + EXPECT_EQ(value.GetType(), AmqpValueType::Bool); + EXPECT_TRUE(value); + + auto val = AmqpValue::Serialize(value); + EXPECT_EQ(1, val.size()); + EXPECT_EQ(0x41, val[0]); + } + { + std::vector testVector{0x42}; + AmqpValue value{AmqpValue::Deserialize(testVector.data(), testVector.size())}; + EXPECT_EQ(value.GetType(), AmqpValueType::Bool); + EXPECT_FALSE(value); + + auto val = AmqpValue::Serialize(value); + EXPECT_EQ(1, val.size()); + EXPECT_EQ(0x42, val[0]); + } +} + +// Test deserializing a UByte value (0x50/0xXX) - section 1.6.3. +TEST_F(TestValueSerialization, SerializeUbyte) +{ + { + std::vector testVector{0x50, 0x25}; + AmqpValue value{AmqpValue::Deserialize(testVector.data(), testVector.size())}; + EXPECT_EQ(value.GetType(), AmqpValueType::Ubyte); + EXPECT_EQ(0x25, static_cast(value)); + + auto val = AmqpValue::Serialize(value); + EXPECT_EQ(2, val.size()); + EXPECT_EQ(0x50, val[0]); + EXPECT_EQ(0x25, val[1]); + } + + { + std::vector testVector{0x50, 0x89}; + AmqpValue value{AmqpValue::Deserialize(testVector.data(), testVector.size())}; + EXPECT_EQ(value.GetType(), AmqpValueType::Ubyte); + EXPECT_EQ(0x89, static_cast(value)); + + auto val = AmqpValue::Serialize(value); + EXPECT_EQ(2, val.size()); + EXPECT_EQ(0x50, val[0]); + EXPECT_EQ(0x89, val[1]); + } +} + +// Test deserializing a UShort value (0x60/0xXX/0xXX) - section 1.6.4. +// Note: Serialized value is in network byte order. +TEST_F(TestValueSerialization, SerializeUShort) +{ + { + std::vector testVector{0x60, 0x00, 0x00}; + AmqpValue value{AmqpValue::Deserialize(testVector.data(), testVector.size())}; + EXPECT_EQ(value.GetType(), AmqpValueType::Ushort); + EXPECT_EQ(0, static_cast(value)); + + auto val = AmqpValue::Serialize(value); + EXPECT_EQ(val, testVector); + } + + { + std::vector testVector{0x60, 0x04, 0x00}; + + AmqpValue value{AmqpValue::Deserialize(testVector.data(), testVector.size())}; + EXPECT_EQ(value.GetType(), AmqpValueType::Ushort); + EXPECT_EQ(0x400, static_cast(value)); + + auto val = AmqpValue::Serialize(value); + EXPECT_EQ(val, testVector); + } + + { + std::vector testVector{0x60, 0x04, 0x80}; + AmqpValue value{AmqpValue::Deserialize(testVector.data(), testVector.size())}; + EXPECT_EQ(value.GetType(), AmqpValueType::Ushort); + EXPECT_EQ(0x480, static_cast(value)); + + auto val = AmqpValue::Serialize(value); + EXPECT_EQ(val, testVector); + } +} + +// Test deserializing a UInt value (0x70/0xXX/0xXX) - section 1.6.5. +// Note that there are three serializations for UInt values: +// The first is as a fixed width value in the form of 0x70/0xXX/0xYY/0xZZ/0xAA with the values in +// network byte order. +// The second applies to values in the range 0..255: 0x52/0xXX +// The third applies to the specific value of 0: 0x43. +TEST_F(TestValueSerialization, SerializeUint) +{ + { + // Input first form with value == 0. Expected output: 3rd form. + std::vector testVector{0x70, 0x00, 0x00, 0x00, 0x00}; + AmqpValue value{AmqpValue::Deserialize(testVector.data(), testVector.size())}; + EXPECT_EQ(value.GetType(), AmqpValueType::Uint); + EXPECT_EQ(0, static_cast(value)); + + auto val = AmqpValue::Serialize(value); + EXPECT_EQ(1, val.size()); + EXPECT_EQ(0x43, val[0]); + } + { + // Third form, value == 0. + std::vector testVector{0x43}; + AmqpValue value{AmqpValue::Deserialize(testVector.data(), testVector.size())}; + EXPECT_EQ(value.GetType(), AmqpValueType::Uint); + EXPECT_EQ(0, static_cast(value)); + + auto val = AmqpValue::Serialize(value); + EXPECT_EQ(1, val.size()); + EXPECT_EQ(0x43, val[0]); + } + { + // First form, value < 255, expected output: 2nd form. + std::vector testVector{0x70, 0x00, 0x00, 0x00, 0x85}; + AmqpValue value{AmqpValue::Deserialize(testVector.data(), testVector.size())}; + EXPECT_EQ(value.GetType(), AmqpValueType::Uint); + EXPECT_EQ(0x85, static_cast(value)); + + auto val = AmqpValue::Serialize(value); + EXPECT_EQ(2, val.size()); + EXPECT_EQ(0x52, val[0]); + EXPECT_EQ(0x85, val[1]); + } + { + // Second form, value < 255. + std::vector testVector{0x52, 0x85}; + AmqpValue value{AmqpValue::Deserialize(testVector.data(), testVector.size())}; + EXPECT_EQ(value.GetType(), AmqpValueType::Uint); + EXPECT_EQ(0x85, static_cast(value)); + + auto val = AmqpValue::Serialize(value); + EXPECT_EQ(2, val.size()); + EXPECT_EQ(0x52, val[0]); + EXPECT_EQ(0x85, val[1]); + } + { + // Second form, value < 255. + std::vector testVector{0x70, 0x12, 0x34, 0x56, 0x78}; + AmqpValue value{AmqpValue::Deserialize(testVector.data(), testVector.size())}; + EXPECT_EQ(value.GetType(), AmqpValueType::Uint); + EXPECT_EQ(0x12345678, static_cast(value)); + + auto val = AmqpValue::Serialize(value); + EXPECT_EQ(val, testVector); + } +} + +// Test deserializing a ULong value - section 1.6.6. +// Note that there are three serializations for UInt values: +// The first is as a fixed width value in the form of 0x80/0xXX/0xYY/0xZZ/0xAA with the values in +// network byte order. +// The second applies to values in the range 0..255: 0x53/0xXX +// The third applies to the specific value of 0: 0x44. +TEST_F(TestValueSerialization, SerializeUlong) +{ + { + // Input first form with value == 0. Expected output: 3rd form. + std::vector testVector{0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + AmqpValue value{AmqpValue::Deserialize(testVector.data(), testVector.size())}; + EXPECT_EQ(value.GetType(), AmqpValueType::Ulong); + EXPECT_EQ(0, static_cast(value)); + + auto val = AmqpValue::Serialize(value); + EXPECT_EQ(1, val.size()); + EXPECT_EQ(0x44, val[0]); + } + { + // Third form, value == 0. + std::vector testVector{0x44}; + AmqpValue value{AmqpValue::Deserialize(testVector.data(), testVector.size())}; + EXPECT_EQ(value.GetType(), AmqpValueType::Ulong); + EXPECT_EQ(0, static_cast(value)); + + auto val = AmqpValue::Serialize(value); + EXPECT_EQ(1, val.size()); + EXPECT_EQ(0x44, val[0]); + } + { + // First form, value < 255, expected output: 2nd form. + std::vector testVector{0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x85}; + AmqpValue value{AmqpValue::Deserialize(testVector.data(), testVector.size())}; + EXPECT_EQ(value.GetType(), AmqpValueType::Ulong); + EXPECT_EQ(0x85, static_cast(value)); + + auto val = AmqpValue::Serialize(value); + EXPECT_EQ(2, val.size()); + EXPECT_EQ(0x53, val[0]); + EXPECT_EQ(0x85, val[1]); + } + { + // Second form, value < 255. + std::vector testVector{0x53, 0x85}; + AmqpValue value{AmqpValue::Deserialize(testVector.data(), testVector.size())}; + EXPECT_EQ(value.GetType(), AmqpValueType::Ulong); + EXPECT_EQ(0x85, static_cast(value)); + + auto val = AmqpValue::Serialize(value); + EXPECT_EQ(2, val.size()); + EXPECT_EQ(0x53, val[0]); + EXPECT_EQ(0x85, val[1]); + } + { + // Second form, value < 255. + std::vector testVector{0x80, 0x12, 0x34, 0x56, 0x78, 0x12, 0x34, 0x56, 0x78}; + AmqpValue value{AmqpValue::Deserialize(testVector.data(), testVector.size())}; + EXPECT_EQ(value.GetType(), AmqpValueType::Ulong); + EXPECT_EQ(0x1234567812345678, static_cast(value)); + + auto val = AmqpValue::Serialize(value); + EXPECT_EQ(val, testVector); + } +} + +// Test deserializing a Byte value (0x51/0xXX) - section 1.6.7. +TEST_F(TestValueSerialization, SerializeByte) +{ + { + std::vector testVector{0x51, 0x25}; + AmqpValue value{AmqpValue::Deserialize(testVector.data(), testVector.size())}; + EXPECT_EQ(value.GetType(), AmqpValueType::Byte); + EXPECT_EQ(0x25, static_cast(value)); + + auto val = AmqpValue::Serialize(value); + EXPECT_EQ(2, val.size()); + EXPECT_EQ(0x51, val[0]); + EXPECT_EQ(0x25, val[1]); + } + + { + std::vector testVector{0x51, 0x89}; + AmqpValue value{AmqpValue::Deserialize(testVector.data(), testVector.size())}; + EXPECT_EQ(value.GetType(), AmqpValueType::Byte); + EXPECT_EQ(-119, static_cast(value)); + + auto val = AmqpValue::Serialize(value); + EXPECT_EQ(2, val.size()); + EXPECT_EQ(0x51, val[0]); + EXPECT_EQ(0x89, val[1]); + } +} + +// Test deserializing a UShort value (0x61/0xXX/0xXX) - section 1.6.8. +// Note: Serialized value is in network byte order. +TEST_F(TestValueSerialization, SerializeShort) +{ + { + std::vector testVector{0x61, 0x00, 0x00}; + AmqpValue value{AmqpValue::Deserialize(testVector.data(), testVector.size())}; + EXPECT_EQ(value.GetType(), AmqpValueType::Short); + EXPECT_EQ(0, static_cast(value)); + + auto val = AmqpValue::Serialize(value); + EXPECT_EQ(val, testVector); + } + + { + std::vector testVector{0x61, 0x04, 0x00}; + + AmqpValue value{AmqpValue::Deserialize(testVector.data(), testVector.size())}; + EXPECT_EQ(value.GetType(), AmqpValueType::Short); + EXPECT_EQ(0x400, static_cast(value)); + + auto val = AmqpValue::Serialize(value); + EXPECT_EQ(val, testVector); + } + + { + std::vector testVector{0x61, 0x04, 0x80}; + AmqpValue value{AmqpValue::Deserialize(testVector.data(), testVector.size())}; + EXPECT_EQ(value.GetType(), AmqpValueType::Short); + EXPECT_EQ(0x480, static_cast(value)); + + auto val = AmqpValue::Serialize(value); + EXPECT_EQ(val, testVector); + } +} + +// Test deserializing a Int value - section 1.6.9. +// Note that there are two serializations for Int values: +// The first is as a fixed width value in the form of 0x71/0xXX/0xYY/0xZZ/0xAA with the values in +// network byte order. +// The second applies to values in the range -128..127: 0x54/0xXX +TEST_F(TestValueSerialization, SerializeInt) +{ + { + // Input first form with value == 0. Expected output: 2nd form. + std::vector testVector{0x71, 0x00, 0x00, 0x00, 0x00}; + AmqpValue value{AmqpValue::Deserialize(testVector.data(), testVector.size())}; + EXPECT_EQ(value.GetType(), AmqpValueType::Int); + EXPECT_EQ(0, static_cast(value)); + + auto val = AmqpValue::Serialize(value); + EXPECT_EQ(2, val.size()); + EXPECT_EQ(0x54, val[0]); + EXPECT_EQ(0x00, val[1]); + } + { + // First form, value < 255, expected output: 2nd form. + std::vector testVector{0x71, 0x00, 0x00, 0x00, 0x75}; + AmqpValue value{AmqpValue::Deserialize(testVector.data(), testVector.size())}; + EXPECT_EQ(value.GetType(), AmqpValueType::Int); + EXPECT_EQ(0x75, static_cast(value)); + + auto val = AmqpValue::Serialize(value); + EXPECT_EQ(2, val.size()); + EXPECT_EQ(0x54, val[0]); + EXPECT_EQ(0x75, val[1]); + } + { + // First form, value < 255, expected output: 2nd form. Note that the value of 0x85 is greater + // than 127 so it cannot be represented in the second form. + std::vector testVector{0x71, 0x00, 0x00, 0x00, 0x85}; + AmqpValue value{AmqpValue::Deserialize(testVector.data(), testVector.size())}; + EXPECT_EQ(value.GetType(), AmqpValueType::Int); + EXPECT_EQ(0x85, static_cast(value)); + + auto val = AmqpValue::Serialize(value); + EXPECT_EQ(val, testVector); + } + { + // Second form, value < 255. + std::vector testVector{0x54, 0x85}; + AmqpValue value{AmqpValue::Deserialize(testVector.data(), testVector.size())}; + EXPECT_EQ(value.GetType(), AmqpValueType::Int); + EXPECT_EQ(-123, static_cast(value)); + + auto val = AmqpValue::Serialize(value); + EXPECT_EQ(2, val.size()); + EXPECT_EQ(0x54, val[0]); + EXPECT_EQ(0x85, val[1]); + } + { + // Second form, value < 255. + std::vector testVector{0x71, 0x12, 0x34, 0x56, 0x78}; + AmqpValue value{AmqpValue::Deserialize(testVector.data(), testVector.size())}; + EXPECT_EQ(value.GetType(), AmqpValueType::Int); + EXPECT_EQ(0x12345678, static_cast(value)); + + auto val = AmqpValue::Serialize(value); + EXPECT_EQ(val, testVector); + } +} + +// Test deserializing a Long value - section 1.6.10. +// Note that there are two serializations for Long values: +// The first is as a fixed width value in the form of 0x81/0xXX/0xYY/0xZZ/0xAA with the values in +// network byte order. +// The second applies to values in the range 0..255: 0x55/0xXX +TEST_F(TestValueSerialization, SerializeLong) +{ + { + // First form, value < 255, expected output: 2nd form. + std::vector testVector{0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x75}; + AmqpValue value{AmqpValue::Deserialize(testVector.data(), testVector.size())}; + EXPECT_EQ(value.GetType(), AmqpValueType::Long); + EXPECT_EQ(0x75, static_cast(value)); + + auto val = AmqpValue::Serialize(value); + EXPECT_EQ(2, val.size()); + EXPECT_EQ(0x55, val[0]); + EXPECT_EQ(0x75, val[1]); + } + { + // First form, value < 255, expected output: First form because 0x85 cannot be expressed as a + // signed byte. + std::vector testVector{0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x85}; + AmqpValue value{AmqpValue::Deserialize(testVector.data(), testVector.size())}; + EXPECT_EQ(value.GetType(), AmqpValueType::Long); + EXPECT_EQ(133, static_cast(value)); + + auto val = AmqpValue::Serialize(value); + EXPECT_EQ(val, testVector); + } + { + // Second form, value < 255. + std::vector testVector{0x55, 0x85}; + AmqpValue value{AmqpValue::Deserialize(testVector.data(), testVector.size())}; + EXPECT_EQ(value.GetType(), AmqpValueType::Long); + EXPECT_EQ(-123, static_cast(value)); + + auto val = AmqpValue::Serialize(value); + EXPECT_EQ(2, val.size()); + EXPECT_EQ(0x55, val[0]); + EXPECT_EQ(0x85, val[1]); + } + { + // Second form, value < 255. + std::vector testVector{0x81, 0x12, 0x34, 0x56, 0x78, 0x12, 0x34, 0x56, 0x78}; + AmqpValue value{AmqpValue::Deserialize(testVector.data(), testVector.size())}; + EXPECT_EQ(value.GetType(), AmqpValueType::Long); + EXPECT_EQ(0x1234567812345678, static_cast(value)); + + auto val = AmqpValue::Serialize(value); + EXPECT_EQ(val, testVector); + } +} + +// Test deserializing a Float value - section 1.6.11. +TEST_F(TestValueSerialization, SerializeFloat) +{ + std::vector testVector{0x72, 0x40, 0x49, 0x0f, 0xda}; + AmqpValue value{AmqpValue::Deserialize(testVector.data(), testVector.size())}; + EXPECT_EQ(value.GetType(), AmqpValueType::Float); + EXPECT_FLOAT_EQ(3.1415926f, static_cast(value)); + + auto val = AmqpValue::Serialize(value); + EXPECT_EQ(val, testVector); +} + +// Test deserializing a Double value - section 1.6.12. +TEST_F(TestValueSerialization, SerializeDouble) +{ + std::vector testVector{0x82, 0x40, 0x09, 0x21, 0xFB, 0x4D, 0x12, 0xD8, 0x4A}; + AmqpValue value{AmqpValue::Deserialize(testVector.data(), testVector.size())}; + EXPECT_EQ(value.GetType(), AmqpValueType::Double); + EXPECT_DOUBLE_EQ(3.1415926, static_cast(value)); + + auto val = AmqpValue::Serialize(value); + EXPECT_EQ(val, testVector); +} + +// Test deserializing a Char value - section 1.6.16. +// Note uAMQP does not appear to have support for encoding and decoding Char values. +#if 0 +TEST_F(TestValueSerialization, SerializeChar) +{ + std::vector testVector{0x73, 0x4D, 0x12, 0xD8, 0x4A}; + AmqpValue value{AmqpValue::Deserialize(testVector.data(), testVector.size())}; + EXPECT_EQ(value.GetType(), AmqpValueType::Char); + EXPECT_EQ(0x4d12d84a, static_cast(value)); + + auto val = AmqpValue::Serialize(value); + EXPECT_EQ(val, testVector); +} +#endif + +// Test deserializing a Milliseconds value - section 1.6.17. +TEST_F(TestValueSerialization, SerializeMilliseconds) +{ + std::vector testVector{0x83, 0x00, 0x00, 0x00, 0x00, 0x64, 0x41, 0xc0, 0x79}; + AmqpValue value{AmqpValue::Deserialize(testVector.data(), testVector.size())}; + EXPECT_EQ(value.GetType(), AmqpValueType::Timestamp); + EXPECT_EQ(0x6441c079, static_cast(value.AsTimestamp()).count()); + + auto val = AmqpValue::Serialize(value); + EXPECT_EQ(val, testVector); +} + +// Test deserializing a Uuid value - section 1.6.18. +TEST_F(TestValueSerialization, SerializeUuid) +{ + Azure::Core::Uuid testUuid{Azure::Core::Uuid::CreateUuid()}; + std::vector testVector{0x98}; + testVector.insert(testVector.end(), testUuid.AsArray().begin(), testUuid.AsArray().end()); + AmqpValue value{AmqpValue::Deserialize(testVector.data(), testVector.size())}; + EXPECT_EQ(value.GetType(), AmqpValueType::Uuid); + EXPECT_EQ(testUuid.ToString(), static_cast(value).ToString()); + + auto val = AmqpValue::Serialize(value); + EXPECT_EQ(val, testVector); +} + +// Test deserializing a Binary value - section 1.6.19. +// Note that there are two serializations for Binary values: +// The first is as a variable width value in the form of 0xa0/<1 byte length>/ +// The second is as a variable width value in the form of 0xb0/<4 byte length>/ +TEST_F(TestValueSerialization, SerializeBinary) +{ + // First form, serialized as first form. + { + Azure::Core::Uuid testUuid{Azure::Core::Uuid::CreateUuid()}; + std::vector testVector{0xa0, 0x10}; + testVector.insert(testVector.end(), testUuid.AsArray().begin(), testUuid.AsArray().end()); + AmqpValue value{AmqpValue::Deserialize(testVector.data(), testVector.size())}; + EXPECT_EQ(value.GetType(), AmqpValueType::Binary); + EXPECT_EQ(value.AsBinary().size(), 16); + auto binary{value.AsBinary()}; + std::array valueAsArray; + + std::copy_n(binary.begin(), 16, valueAsArray.begin()); + EXPECT_EQ(valueAsArray, testUuid.AsArray()); + + auto val = AmqpValue::Serialize(value); + EXPECT_EQ(val, testVector); + } + // Second form, serialized as first form. + { + Azure::Core::Uuid testUuid{Azure::Core::Uuid::CreateUuid()}; + std::vector testVector{0xb0, 0x00, 0x00, 0x00, 0x10}; + testVector.insert(testVector.end(), testUuid.AsArray().begin(), testUuid.AsArray().end()); + AmqpValue value{AmqpValue::Deserialize(testVector.data(), testVector.size())}; + EXPECT_EQ(value.GetType(), AmqpValueType::Binary); + EXPECT_EQ(value.AsBinary().size(), 16); + auto binary{value.AsBinary()}; + std::array valueAsArray; + + std::copy_n(binary.begin(), 16, valueAsArray.begin()); + EXPECT_EQ(valueAsArray, testUuid.AsArray()); + + auto val = AmqpValue::Serialize(value); + EXPECT_EQ(18, val.size()); + EXPECT_EQ(0xa0, val[0]); + EXPECT_EQ(0x10, val[1]); + } + + { + char values[255]{}; + std::vector testVector{0xa0, sizeof(values)}; + testVector.insert(testVector.end(), values, values + sizeof(values)); + AmqpValue value{AmqpValue::Deserialize(testVector.data(), testVector.size())}; + EXPECT_EQ(value.GetType(), AmqpValueType::Binary); + EXPECT_EQ(value.AsBinary().size(), 255); + + auto val = AmqpValue::Serialize(value); + EXPECT_EQ(257, val.size()); + EXPECT_EQ(0xa0, val[0]); + EXPECT_EQ(0xff, val[1]); + } + { + char values[256]{}; + std::vector testVector{0xb0, 0x00, 0x00, 0x01, 0x00}; + testVector.insert(testVector.end(), values, values + sizeof(values)); + AmqpValue value{AmqpValue::Deserialize(testVector.data(), testVector.size())}; + EXPECT_EQ(value.GetType(), AmqpValueType::Binary); + EXPECT_EQ(value.AsBinary().size(), 256); + + auto val = AmqpValue::Serialize(value); + EXPECT_EQ(261, val.size()); + EXPECT_EQ(0xb0, val[0]); + EXPECT_EQ(0x00, val[1]); + EXPECT_EQ(0x00, val[2]); + EXPECT_EQ(0x01, val[3]); + EXPECT_EQ(0x00, val[4]); + } +} + +// Test deserializing a String value - section 1.6.20. +// Note that there are two serializations for String values: +// The first is as a variable width value in the form of 0xa1/<1 byte length>/ +// The second is as a variable width value in the form of 0xb1/<4 byte length>/ +TEST_F(TestValueSerialization, SerializeString) +{ + // First form, serialized as first form. + { + std::string stringValue; + for (int i = 0; i < 255; i += 1) + { + stringValue.push_back("ABCDEFGHIJKLMNOPQRSTUVWXYZ"[i % 25]); + } + std::vector testVector{0xa1, 0xff}; + testVector.insert(testVector.end(), stringValue.begin(), stringValue.end()); + AmqpValue value{AmqpValue::Deserialize(testVector.data(), testVector.size())}; + EXPECT_EQ(value.GetType(), AmqpValueType::String); + EXPECT_EQ(static_cast(value).size(), 255); + EXPECT_EQ(stringValue, static_cast(value)); + + auto val = AmqpValue::Serialize(value); + EXPECT_EQ(val, testVector); + } + // Second form, serialized as first form. + { + std::string stringValue; + for (int i = 0; i < 255; i += 1) + { + stringValue.push_back("ABCDEFGHIJKLMNOPQRSTUVWXYZ"[i % 26]); + } + std::vector testVector{0xb1, 0x00, 0x00, 0x00, 0xff}; + testVector.insert(testVector.end(), stringValue.begin(), stringValue.end()); + AmqpValue value{AmqpValue::Deserialize(testVector.data(), testVector.size())}; + EXPECT_EQ(value.GetType(), AmqpValueType::String); + EXPECT_EQ(static_cast(value).size(), 255); + EXPECT_EQ(stringValue, static_cast(value)); + + auto val = AmqpValue::Serialize(value); + EXPECT_EQ(val.size(), 257); + EXPECT_EQ(val[0], 0xa1); + EXPECT_EQ(val[1], 0xff); + EXPECT_EQ(val[2], 'A'); + } + // Second form, serialized as second form. + { + std::string stringValue; + for (int i = 0; i < 256; i += 1) + { + stringValue.push_back("ABCDEFGHIJKLMNOPQRSTUVWXYZ"[i % 25]); + } + std::vector testVector{0xb1, 0x00, 0x00, 0x01, 0x00}; + testVector.insert(testVector.end(), stringValue.begin(), stringValue.end()); + AmqpValue value{AmqpValue::Deserialize(testVector.data(), testVector.size())}; + EXPECT_EQ(value.GetType(), AmqpValueType::String); + EXPECT_EQ(static_cast(value).size(), 256); + EXPECT_EQ(stringValue, static_cast(value)); + + auto val = AmqpValue::Serialize(value); + EXPECT_EQ(val, testVector); + } +} + +// Test deserializing a Symbol value - section 1.6.21. +// Note that there are two serializations for Symbol values: +// The first is as a variable width value in the form of 0xa3/<1 byte length>/ +// The second is as a variable width value in the form of 0xb3/<4 byte length>/ +TEST_F(TestValueSerialization, SerializeSymbol) +{ + // First form, serialized as first form. + { + std::string stringValue; + for (int i = 0; i < 255; i += 1) + { + stringValue.push_back("ABCDEFGHIJKLMNOPQRSTUVWXYZ"[i % 25]); + } + std::vector testVector{0xa3, 0xff}; + testVector.insert(testVector.end(), stringValue.begin(), stringValue.end()); + AmqpValue value{AmqpValue::Deserialize(testVector.data(), testVector.size())}; + EXPECT_EQ(value.GetType(), AmqpValueType::Symbol); + EXPECT_EQ(value.AsSymbol().size(), 255); + EXPECT_EQ(AmqpSymbol(stringValue), value.AsSymbol()); + + auto val = AmqpValue::Serialize(value); + EXPECT_EQ(val, testVector); + } + // Second form, serialized as first form. + { + std::string stringValue; + for (int i = 0; i < 255; i += 1) + { + stringValue.push_back("ABCDEFGHIJKLMNOPQRSTUVWXYZ"[i % 26]); + } + std::vector testVector{0xb3, 0x00, 0x00, 0x00, 0xff}; + testVector.insert(testVector.end(), stringValue.begin(), stringValue.end()); + AmqpValue value{AmqpValue::Deserialize(testVector.data(), testVector.size())}; + EXPECT_EQ(value.GetType(), AmqpValueType::Symbol); + EXPECT_EQ(value.AsSymbol().size(), 255); + EXPECT_EQ(AmqpSymbol(stringValue), value.AsSymbol()); + + auto val = AmqpValue::Serialize(value); + EXPECT_EQ(val.size(), 257); + EXPECT_EQ(val[0], 0xa3); + EXPECT_EQ(val[1], 0xff); + EXPECT_EQ(val[2], 'A'); + } + // Second form, serialized as second form. + { + std::string stringValue; + for (int i = 0; i < 256; i += 1) + { + stringValue.push_back("ABCDEFGHIJKLMNOPQRSTUVWXYZ"[i % 26]); + } + std::vector testVector{0xb3, 0x00, 0x00, 0x01, 0x00}; + testVector.insert(testVector.end(), stringValue.begin(), stringValue.end()); + AmqpValue value{AmqpValue::Deserialize(testVector.data(), testVector.size())}; + EXPECT_EQ(value.GetType(), AmqpValueType::Symbol); + EXPECT_EQ(value.AsSymbol().size(), 256); + EXPECT_EQ(AmqpSymbol(stringValue), value.AsSymbol()); + + auto val = AmqpValue::Serialize(value); + EXPECT_EQ(val, testVector); + } +} + +template T GenerateRandomValue() +{ + // Generate a random value of type T using std::random_device. + // This is not a cryptographically secure random number generator, but it is good enough for our + // purposes. The random number generator is seeded with a random value from std::random_device. + // The random number generator is then used to generate a random value of type T. + std::random_device rd; + std::mt19937 gen(rd()); + std::uniform_int_distribution dis( + std::numeric_limits::min(), std::numeric_limits::max()); + return dis(gen); +} + +// Append a value of type T to a buffer. +// The value is appended in network byte order. +// The value is appended as a fixed width value. +// T +void AppendValue(std::vector& buffer, uint16_t value) +{ + buffer.push_back((value >> 8) & 0xff); + buffer.push_back(value & 0xff); +} +void AppendValue(std::vector& buffer, uint32_t value) +{ + buffer.push_back((value >> 24) & 0xff); + buffer.push_back((value >> 16) & 0xff); + buffer.push_back((value >> 8) & 0xff); + buffer.push_back(value & 0xff); +} + +// Test deserializing a List value - section 1.6.22. +// Note that there are three serializations for List values: +// The first is a fixed value in the form of 0x45 which represents an empty list. +// The second is a compound value in the form of 0xc0/<1 byte size>/<1 byte count>/ +// for list elements with a total size less than 255 octets. The third is a compound value in the +// form of 0xd0/<4 byte size in network byte order>/<4 byte count in network byte order>/ for list elements with a total size less than 2^32 octets. +TEST_F(TestValueSerialization, SerializeList) +{ + // First form, serialized as first form. + { + std::vector testVector{0x45}; + AmqpValue value{AmqpValue::Deserialize(testVector.data(), testVector.size())}; + AmqpList list{value.AsList()}; + EXPECT_EQ(list.size(), 0); + } + + // First form, serialized as first form. + { + AmqpList emptyList; + AmqpValue value{emptyList}; + std::vector val = AmqpValue::Serialize(value); + EXPECT_EQ(val.size(), 1); + EXPECT_EQ(0x45, val[0]); + } + + // Second form, serialized as first form. + { + std::vector testVector{0xc0, 0x01, 0x00}; + AmqpValue value{AmqpValue::Deserialize(testVector.data(), testVector.size())}; + EXPECT_EQ(value.GetType(), AmqpValueType::List); + AmqpList list{value.AsList()}; + EXPECT_EQ(list.size(), 0); + + std::vector val = AmqpValue::Serialize(value); + EXPECT_EQ(val.size(), 1); + EXPECT_EQ(0x45, val[0]); + } + // THird form, serialized as first form. + { + std::vector testVector{0xd0, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00}; + AmqpValue value{AmqpValue::Deserialize(testVector.data(), testVector.size())}; + EXPECT_EQ(value.GetType(), AmqpValueType::List); + AmqpList list{value.AsList()}; + EXPECT_EQ(list.size(), 0); + + std::vector val = AmqpValue::Serialize(value); + EXPECT_EQ(val.size(), 1); + EXPECT_EQ(0x45, val[0]); + } + + // Second form serialized as second form. + { + constexpr size_t valueCount = 0x15; + // Create an array of random values. + std::vector values; + for (int i = 0; i < valueCount; i += 1) + { + values.push_back(AmqpValue(static_cast(GenerateRandomValue()))); + } + size_t totalSize = 1; // Include the size of the list count in the size + for (auto const& val : values) + { + totalSize += AmqpValue::GetSerializedSize(val); + } + EXPECT_LE(totalSize, 255); + std::vector testVector{0xc0}; + testVector.push_back(static_cast(totalSize)); + testVector.push_back(static_cast(valueCount)); + + // Now append all the values to the list. + for (auto const& val : values) + { + auto serializedVal{AmqpValue::Serialize(val)}; + testVector.insert(testVector.end(), serializedVal.begin(), serializedVal.end()); + } + + AmqpValue value{AmqpValue::Deserialize(testVector.data(), testVector.size())}; + EXPECT_EQ(value.GetType(), AmqpValueType::List); + AmqpList list{value.AsList()}; + EXPECT_EQ(list.size(), values.size()); + EXPECT_TRUE(std::equal(values.begin(), values.end(), list.begin(), list.end())); + + auto val = AmqpValue::Serialize(value); + EXPECT_EQ(val, testVector); + } + + // Third form, serialized as second form. + { + constexpr size_t valueCount = 0x10; + // Create an array of random values. + std::vector values; + for (int i = 0; i < valueCount; i += 1) + { + values.push_back(AmqpValue(static_cast(GenerateRandomValue()))); + } + size_t totalSize = 4; // Include the size of the list count in the size + for (auto const& val : values) + { + totalSize += AmqpValue::GetSerializedSize(val); + } + EXPECT_LE(totalSize, 255); + std::vector testVector{0xd0}; + AppendValue(testVector, static_cast(totalSize)); + AppendValue(testVector, static_cast(valueCount)); + + // Now append all the values to the list. + for (auto const& val : values) + { + auto serializedVal{AmqpValue::Serialize(val)}; + testVector.insert(testVector.end(), serializedVal.begin(), serializedVal.end()); + } + + AmqpValue value{AmqpValue::Deserialize(testVector.data(), testVector.size())}; + EXPECT_EQ(value.GetType(), AmqpValueType::List); + AmqpList list{value.AsList()}; + EXPECT_EQ(list.size(), values.size()); + EXPECT_TRUE(std::equal(values.begin(), values.end(), list.begin(), list.end())); + + auto val = AmqpValue::Serialize(value); + EXPECT_EQ(val.size(), 147); + EXPECT_EQ(val[0], 0xc0); + EXPECT_EQ( + val[1], + totalSize - 3 /* Account for the difference in size between the input and output size.*/); + EXPECT_EQ(val[2], valueCount); + } + + // Third form, serialized as third form. + { + constexpr size_t valueCount = 0x210; + // Create an array of random values. + std::vector values; + for (int i = 0; i < valueCount; i += 1) + { + values.push_back(AmqpValue(static_cast(GenerateRandomValue()))); + } + size_t totalSize = 4; // Include the size of the list count in the size + for (auto const& val : values) + { + totalSize += AmqpValue::GetSerializedSize(val); + } + EXPECT_GE(totalSize, 256); + std::vector testVector{0xd0}; + AppendValue(testVector, static_cast(totalSize)); + AppendValue(testVector, static_cast(valueCount)); + + // Now append all the values to the list. + for (auto const& val : values) + { + auto serializedVal{AmqpValue::Serialize(val)}; + testVector.insert(testVector.end(), serializedVal.begin(), serializedVal.end()); + } + + AmqpValue value{AmqpValue::Deserialize(testVector.data(), testVector.size())}; + EXPECT_EQ(value.GetType(), AmqpValueType::List); + AmqpList list{value.AsList()}; + EXPECT_EQ(list.size(), values.size()); + EXPECT_TRUE(std::equal(values.begin(), values.end(), list.begin(), list.end())); + + auto val = AmqpValue::Serialize(value); + EXPECT_EQ(val, testVector); + } +} + +// Test deserializing an Array value - section 1.6.24. +// Note that there are two serializations for Arrayvalues: +// The first is an array value in the form of 0xe0/<1 byte count>/ +// for arrays with a total count of less than 255 octets. The second is an array value in the +// form of 0xf0/<4 byte count in network byte order>/ for list elements with a total size less than 2^32 octets. +TEST_F(TestValueSerialization, SerializeArray) +{ + // First form, serialized as first form. + { + std::vector testVector{0xe0, 0x01, 0x00}; + AmqpValue value{AmqpValue::Deserialize(testVector.data(), testVector.size())}; + EXPECT_EQ(value.GetType(), AmqpValueType::Array); + AmqpArray array{value.AsArray()}; + EXPECT_EQ(array.size(), 0); + + std::vector val = AmqpValue::Serialize(value); + EXPECT_EQ(testVector, val); + } + // Second form, serialized as first form. + { + std::vector testVector{0xf0, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00}; + AmqpValue value{AmqpValue::Deserialize(testVector.data(), testVector.size())}; + EXPECT_EQ(value.GetType(), AmqpValueType::Array); + AmqpArray array{value.AsArray()}; + EXPECT_EQ(array.size(), 0); + + std::vector val = AmqpValue::Serialize(value); + EXPECT_EQ(val.size(), 3); + EXPECT_EQ(0xe0, val[0]); + EXPECT_EQ(0x01, val[1]); + EXPECT_EQ(0x00, val[2]); + } + + // Second form, serialized as first form. + { + constexpr size_t valueCount = 0x10; + // Create an array of random values. + std::vector values; + for (int i = 0; i < valueCount; i += 1) + { + values.push_back(AmqpValue(static_cast(GenerateRandomValue()))); + } + size_t totalSize = 4 + 1; // Include the size of the list count in the size + for (auto const& val : values) + { + totalSize + += (AmqpValue::GetSerializedSize(val) + - 1); // We're not going to serialize the constructor on the serialized values. + } + EXPECT_LE(totalSize, 255); + std::vector testVector{0xf0}; + AppendValue(testVector, static_cast(totalSize)); + AppendValue(testVector, static_cast(valueCount)); + testVector.push_back(0x81); // Insert the constructor for the elements. + + // Now append all the values to the list. + for (auto const& val : values) + { + auto serializedVal{AmqpValue::Serialize(val)}; + // We want to skip over the constructor value in the serialized constructor. + testVector.insert(testVector.end(), serializedVal.begin() + 1, serializedVal.end()); + } + + AmqpValue value{AmqpValue::Deserialize(testVector.data(), testVector.size())}; + EXPECT_EQ(value.GetType(), AmqpValueType::Array); + AmqpArray array{value.AsArray()}; + EXPECT_EQ(array.size(), values.size()); + EXPECT_TRUE(std::equal(values.begin(), values.end(), array.begin(), array.end())); + + auto val = AmqpValue::Serialize(value); + EXPECT_EQ(val.size(), 132); + EXPECT_EQ(val[0], 0xe0); + EXPECT_EQ( + val[1], + totalSize - 3 /* Account for the difference in size between the input and output size.*/); + EXPECT_EQ(val[2], valueCount); + } + + // Second form, serialized as second form. + { + constexpr size_t valueCount = 0x210; + // Create an array of random values. + std::vector values; + for (int i = 0; i < valueCount; i += 1) + { + values.push_back(AmqpValue(static_cast(GenerateRandomValue()))); + } + size_t totalSize = 4 + 1; // Include the size of the list count in the size + for (auto const& val : values) + { + totalSize += (AmqpValue::GetSerializedSize(val) - 1); + } + EXPECT_GE(totalSize, 256); + std::vector testVector{0xf0}; + AppendValue(testVector, static_cast(totalSize)); + AppendValue(testVector, static_cast(valueCount)); + testVector.push_back(0x81); // element constructor - 0x80 == long. + + // Now append all the values to the list. + for (auto const& val : values) + { + auto serializedVal{AmqpValue::Serialize(val)}; + testVector.insert(testVector.end(), serializedVal.begin() + 1, serializedVal.end()); + } + + AmqpValue value{AmqpValue::Deserialize(testVector.data(), testVector.size())}; + EXPECT_EQ(value.GetType(), AmqpValueType::Array); + AmqpArray array{value.AsArray()}; + EXPECT_EQ(array.size(), values.size()); + size_t i = 0; + for (auto const& val : array) + { + if (val != values[i]) + { + GTEST_LOG_(ERROR) << "Mismatch in decoded array at offset " << i + << "Original value: " << values[i] << " Transformed value: " << val; + } + i += 1; + } + EXPECT_TRUE(std::equal(values.begin(), values.end(), array.begin(), array.end())); + + auto val = AmqpValue::Serialize(value); + EXPECT_EQ(val, testVector); + } +} + +// Test deserializing an Map value - section 1.6.23. +// Note that there are two serializations for Map values: +// The first is an array value in the form of 0xc1/<1 byte count>/ +// for maps with a total count of less than 255 values. The second is a value in the +// form of 0xd1/<4 byte count in network byte order>/ for map elements with a total size less than 2^32 octets. +TEST_F(TestValueSerialization, SerializeMap) +{ + // First form, serialized as first form. + { + std::vector testVector{0xc1, 0x01, 0x00}; + AmqpValue value{AmqpValue::Deserialize(testVector.data(), testVector.size())}; + EXPECT_EQ(value.GetType(), AmqpValueType::Map); + AmqpMap map{value.AsMap()}; + EXPECT_EQ(map.size(), 0); + + std::vector val = AmqpValue::Serialize(value); + EXPECT_EQ(testVector, val); + } + + // Second form, serialized as first form. + { + std::vector testVector{0xd1, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00}; + AmqpValue value{AmqpValue::Deserialize(testVector.data(), testVector.size())}; + EXPECT_EQ(value.GetType(), AmqpValueType::Map); + AmqpMap map{value.AsMap()}; + EXPECT_EQ(map.size(), 0); + + std::vector val = AmqpValue::Serialize(value); + EXPECT_EQ(val.size(), 3); + EXPECT_EQ(0xc1, val[0]); + EXPECT_EQ(0x01, val[1]); + EXPECT_EQ(0x00, val[2]); + } + + // Second form, serialized as first form. + { + constexpr size_t valueCount = 0x10; + // Create an map of random values. + std::map values; + for (int i = 0; i < valueCount; i += 1) + { + values.emplace( + AmqpValue(std::to_string(i)), + AmqpValue(static_cast(GenerateRandomValue()))); + } + size_t totalSize = 4; // Include the size of the list count in the size + for (auto const& val : values) + { + totalSize + += (AmqpValue::GetSerializedSize(val.first) + + AmqpValue::GetSerializedSize(val.second)); // We're not going to serialize the + // constructor on the serialized values. + } + EXPECT_LE(totalSize, 255); + std::vector testVector{0xd1}; + AppendValue(testVector, static_cast(totalSize)); + AppendValue(testVector, static_cast(valueCount * 2)); + + // Now append all the values to the list. + for (auto const& val : values) + { + { + auto serializedVal{AmqpValue::Serialize(val.first)}; + testVector.insert(testVector.end(), serializedVal.begin(), serializedVal.end()); + } + { + auto serializedVal{AmqpValue::Serialize(val.second)}; + testVector.insert(testVector.end(), serializedVal.begin(), serializedVal.end()); + } + } + + AmqpValue value{AmqpValue::Deserialize(testVector.data(), testVector.size())}; + EXPECT_EQ(value.GetType(), AmqpValueType::Map); + AmqpMap map{value.AsMap()}; + EXPECT_EQ(map.size(), values.size()); + EXPECT_TRUE(std::equal(values.begin(), values.end(), map.begin(), map.end())); + + auto val = AmqpValue::Serialize(value); + EXPECT_EQ(val.size(), 201); + EXPECT_EQ(val[0], 0xc1); + EXPECT_EQ(val[1], totalSize - 3); + EXPECT_EQ(val[2], valueCount * 2); + } + + // Second form, serialized as second form. + { + constexpr size_t valueCount = 0x210; + // Create an map of random values. + std::map values; + for (int i = 0; i < valueCount; i += 1) + { + values.emplace( + AmqpValue(std::to_string(i)), + AmqpValue(static_cast(GenerateRandomValue()))); + } + size_t totalSize = 4; // Include the size of the list count in the size + for (auto const& val : values) + { + totalSize + += (AmqpValue::GetSerializedSize(val.first) + AmqpValue::GetSerializedSize(val.second)); + } + EXPECT_GE(totalSize, 256); + std::vector testVector{0xd1}; + AppendValue(testVector, static_cast(totalSize)); + AppendValue(testVector, static_cast(valueCount * 2)); + + // Now append all the values to the list. + for (auto const& val : values) + { + { + auto serializedVal{AmqpValue::Serialize(val.first)}; + testVector.insert(testVector.end(), serializedVal.begin(), serializedVal.end()); + } + { + auto serializedVal{AmqpValue::Serialize(val.second)}; + testVector.insert(testVector.end(), serializedVal.begin(), serializedVal.end()); + } + } + + AmqpValue value{AmqpValue::Deserialize(testVector.data(), testVector.size())}; + EXPECT_EQ(value.GetType(), AmqpValueType::Map); + AmqpMap map{value.AsMap()}; + EXPECT_EQ(map.size(), values.size()); + int i = 0; + auto valIterator = values.begin(); + for (auto const& val : map) + { + EXPECT_NE(valIterator, values.end()); + if (val.first != valIterator->first) + { + GTEST_LOG_(ERROR) << "Key Mismatch in decoded map at offset " << i + << ". Original key: " << i << " Transformed key: " << val.first; + } + if (val.second != valIterator->second) + { + GTEST_LOG_(ERROR) << "Value Mismatch in decoded map at offset " << i + << ". Original value: " << values[i] + << " Transformed value: " << val.first; + } + i += 1; + valIterator++; + } + EXPECT_TRUE(std::equal(values.begin(), values.end(), map.begin(), map.end())); + + auto val = AmqpValue::Serialize(value); + EXPECT_EQ(val, testVector); + } +} diff --git a/sdk/core/azure-core-amqp/test/ut/mock_amqp_server.hpp b/sdk/core/azure-core-amqp/test/ut/mock_amqp_server.hpp index 381f0c3f24..666c55e2ff 100644 --- a/sdk/core/azure-core-amqp/test/ut/mock_amqp_server.hpp +++ b/sdk/core/azure-core-amqp/test/ut/mock_amqp_server.hpp @@ -14,14 +14,11 @@ #include #include #include +#include extern uint16_t FindAvailableSocket(); namespace MessageTests { -// AMQP ApplicationProperties descriptor (AMQP Specification 1.0 section 3.2.5) -// http://docs.oasis-open.org/amqp/core/v1.0/os/amqp-core-messaging-v1.0-os.html#type-application-properties -constexpr uint64_t AmqpApplicationPropertiesDescriptor = 116; - class AmqpServerMock : public Azure::Core::Amqp::Network::_internal::SocketListenerEvents, public Azure::Core::Amqp::_internal::ConnectionEvents, public Azure::Core::Amqp::_internal::SessionEvents, From 482133d2f6798fde2a41d3794b3bc765b6aa195b Mon Sep 17 00:00:00 2001 From: Larry Osterman Date: Wed, 26 Apr 2023 17:03:52 -0700 Subject: [PATCH 02/17] clang fix --- sdk/core/azure-core-amqp/src/amqp/session.cpp | 355 +++++++++--------- 1 file changed, 178 insertions(+), 177 deletions(-) diff --git a/sdk/core/azure-core-amqp/src/amqp/session.cpp b/sdk/core/azure-core-amqp/src/amqp/session.cpp index 0f50eeb78d..f409517ee4 100644 --- a/sdk/core/azure-core-amqp/src/amqp/session.cpp +++ b/sdk/core/azure-core-amqp/src/amqp/session.cpp @@ -16,183 +16,184 @@ void Azure::Core::_internal::UniqueHandleHelper::FreeAmqpS } namespace Azure { namespace Core { namespace Amqp { - namespace _internal { - Endpoint::~Endpoint() - { - if (m_endpoint) - { - connection_destroy_endpoint(m_endpoint); - } - } - - Session::Session( - Connection const& parentConnection, - Endpoint& newEndpoint, - SessionEvents* eventHandler) - : m_impl{std::make_shared( - parentConnection.GetImpl(), - newEndpoint, - eventHandler)} - { - } - - Session::Session(Connection const& parentConnection, SessionEvents* eventHandler) - : m_impl{std::make_shared( - parentConnection.GetImpl(), - eventHandler)} - { - } - - Session::~Session() noexcept {} - - void Session::SetIncomingWindow(uint32_t incomingWindow) - { - m_impl->SetIncomingWindow(incomingWindow); - } - uint32_t Session::GetIncomingWindow() const { return m_impl->GetIncomingWindow(); } - void Session::SetOutgoingWindow(uint32_t outgoingWindow) - { - m_impl->SetOutgoingWindow(outgoingWindow); - } - uint32_t Session::GetOutgoingWindow() const { return m_impl->GetOutgoingWindow(); } - void Session::SetHandleMax(uint32_t handleMax) { m_impl->SetHandleMax(handleMax); } - uint32_t Session::GetHandleMax() const { return m_impl->GetHandleMax(); } - - void Session::Begin() { m_impl->Begin(); } - void Session::End(std::string const& condition_value, std::string const& description) - { - m_impl->End(condition_value, description); - } - } // namespace _internal - - namespace _detail { - - SessionImpl::~SessionImpl() noexcept {} - - SessionImpl::SessionImpl( - std::shared_ptr<_detail::ConnectionImpl> connection, - _internal::Endpoint& endpoint, - _internal::SessionEvents* eventHandler) - : m_session{session_create_from_endpoint( - *connection, - endpoint.Release(), - SessionImpl::OnLinkAttachedFn, - this)}, - m_connectionToPoll(connection), m_eventHandler{eventHandler} - { - } - - SessionImpl::SessionImpl( - std::shared_ptr<_detail::ConnectionImpl> connection, - _internal::SessionEvents* eventHandler) - : m_session{session_create(*connection, SessionImpl::OnLinkAttachedFn, this)}, - m_connectionToPoll(connection), m_eventHandler{eventHandler} - - { - } - - void SessionImpl::SetIncomingWindow(uint32_t window) - { - if (session_set_incoming_window(m_session.get(), window)) - { - throw std::runtime_error("Could not set incoming window"); // LCOV_EXCL_LINE - } - } - uint32_t SessionImpl::GetIncomingWindow() - { - uint32_t window; - if (session_get_incoming_window(m_session.get(), &window)) - { - throw std::runtime_error("Could not get incoming window"); // LCOV_EXCL_LINE - } - return window; - } - - void SessionImpl::SetOutgoingWindow(uint32_t window) - { - if (session_set_outgoing_window(m_session.get(), window)) - { - throw std::runtime_error("Could not set outgoing window"); // LCOV_EXCL_LINE - } - } - - uint32_t SessionImpl::GetOutgoingWindow() - { - uint32_t window; - if (session_get_outgoing_window(m_session.get(), &window)) - { - throw std::runtime_error("Could not get outgoing window"); // LCOV_EXCL_LINE - } - return window; - } - - void SessionImpl::SetHandleMax(uint32_t max) - { - if (session_set_handle_max(m_session.get(), max)) - { - throw std::runtime_error("Could not set handle max."); // LCOV_EXCL_LINE - } - } - uint32_t SessionImpl::GetHandleMax() - { - uint32_t max; - if (session_get_handle_max(m_session.get(), &max)) - { - throw std::runtime_error("Could not get handle max."); // LCOV_EXCL_LINE - } - return max; - } - - void SessionImpl::Begin() - { - if (session_begin(m_session.get())) - { - throw std::runtime_error("Could not begin session"); // LCOV_EXCL_LINE - } - } - void SessionImpl::End(const std::string& condition, const std::string& description) - { - // When we end the session, it clears all the links, so we need to ensure that the - // m_newLinkAttachedQueue.Clear(); - if (session_end( - m_session.get(), - condition.empty() ? nullptr : condition.c_str(), - description.empty() ? nullptr : description.c_str())) - { - throw std::runtime_error("Could not begin session"); // LCOV_EXCL_LINE - } - } - - bool SessionImpl::OnLinkAttachedFn( - void* context, - LINK_ENDPOINT_INSTANCE_TAG* newLinkEndpoint, - const char* name, - bool role, - AMQP_VALUE_DATA_TAG* source, - AMQP_VALUE_DATA_TAG* target, - AMQP_VALUE_DATA_TAG* properties) - { - SessionImpl* session = static_cast(context); - _internal::LinkEndpoint linkEndpoint(newLinkEndpoint); - if (session->m_eventHandler) - { - return session->m_eventHandler->OnLinkAttached( - session->shared_from_this(), - linkEndpoint, - name, - role == role_receiver ? Azure::Core::Amqp::_internal::SessionRole::Receiver - : Azure::Core::Amqp::_internal::SessionRole::Sender, - source, - target, - properties); + namespace _internal { + Endpoint::~Endpoint() + { + if (m_endpoint) + { + connection_destroy_endpoint(m_endpoint); + } + } + + Session::Session( + Connection const& parentConnection, + Endpoint& newEndpoint, + SessionEvents* eventHandler) + : m_impl{std::make_shared( + parentConnection.GetImpl(), + newEndpoint, + eventHandler)} + { + } + + Session::Session(Connection const& parentConnection, SessionEvents* eventHandler) + : m_impl{std::make_shared( + parentConnection.GetImpl(), + eventHandler)} + { + } + + Session::~Session() noexcept {} + + void Session::SetIncomingWindow(uint32_t incomingWindow) + { + m_impl->SetIncomingWindow(incomingWindow); } - else + uint32_t Session::GetIncomingWindow() const { return m_impl->GetIncomingWindow(); } + void Session::SetOutgoingWindow(uint32_t outgoingWindow) + { + m_impl->SetOutgoingWindow(outgoingWindow); + } + uint32_t Session::GetOutgoingWindow() const { return m_impl->GetOutgoingWindow(); } + void Session::SetHandleMax(uint32_t handleMax) { m_impl->SetHandleMax(handleMax); } + uint32_t Session::GetHandleMax() const { return m_impl->GetHandleMax(); } + + void Session::Begin() { m_impl->Begin(); } + void Session::End(std::string const& condition_value, std::string const& description) + { + m_impl->End(condition_value, description); + } + } // namespace _internal + + namespace _detail { + + SessionImpl::~SessionImpl() noexcept {} + + SessionImpl::SessionImpl( + std::shared_ptr<_detail::ConnectionImpl> connection, + _internal::Endpoint& endpoint, + _internal::SessionEvents* eventHandler) + : m_connectionToPoll(connection), m_session{session_create_from_endpoint( + *connection, + endpoint.Release(), + SessionImpl::OnLinkAttachedFn, + this)}, + m_eventHandler{eventHandler} + { + } + + SessionImpl::SessionImpl( + std::shared_ptr<_detail::ConnectionImpl> connection, + _internal::SessionEvents* eventHandler) + : m_connectionToPoll(connection), + m_session{session_create(*connection, SessionImpl::OnLinkAttachedFn, this)}, + m_eventHandler{eventHandler} + + { + } + + void SessionImpl::SetIncomingWindow(uint32_t window) { - // Even if we don't have any actions to take, if we return false, the connection will be - // aborted. - return true; - } - static_cast(role); - } - } // namespace _detail + if (session_set_incoming_window(m_session.get(), window)) + { + throw std::runtime_error("Could not set incoming window"); // LCOV_EXCL_LINE + } + } + uint32_t SessionImpl::GetIncomingWindow() + { + uint32_t window; + if (session_get_incoming_window(m_session.get(), &window)) + { + throw std::runtime_error("Could not get incoming window"); // LCOV_EXCL_LINE + } + return window; + } + + void SessionImpl::SetOutgoingWindow(uint32_t window) + { + if (session_set_outgoing_window(m_session.get(), window)) + { + throw std::runtime_error("Could not set outgoing window"); // LCOV_EXCL_LINE + } + } + + uint32_t SessionImpl::GetOutgoingWindow() + { + uint32_t window; + if (session_get_outgoing_window(m_session.get(), &window)) + { + throw std::runtime_error("Could not get outgoing window"); // LCOV_EXCL_LINE + } + return window; + } + + void SessionImpl::SetHandleMax(uint32_t max) + { + if (session_set_handle_max(m_session.get(), max)) + { + throw std::runtime_error("Could not set handle max."); // LCOV_EXCL_LINE + } + } + uint32_t SessionImpl::GetHandleMax() + { + uint32_t max; + if (session_get_handle_max(m_session.get(), &max)) + { + throw std::runtime_error("Could not get handle max."); // LCOV_EXCL_LINE + } + return max; + } + + void SessionImpl::Begin() + { + if (session_begin(m_session.get())) + { + throw std::runtime_error("Could not begin session"); // LCOV_EXCL_LINE + } + } + void SessionImpl::End(const std::string& condition, const std::string& description) + { + // When we end the session, it clears all the links, so we need to ensure that the + // m_newLinkAttachedQueue.Clear(); + if (session_end( + m_session.get(), + condition.empty() ? nullptr : condition.c_str(), + description.empty() ? nullptr : description.c_str())) + { + throw std::runtime_error("Could not begin session"); // LCOV_EXCL_LINE + } + } + + bool SessionImpl::OnLinkAttachedFn( + void* context, + LINK_ENDPOINT_INSTANCE_TAG* newLinkEndpoint, + const char* name, + bool role, + AMQP_VALUE_DATA_TAG* source, + AMQP_VALUE_DATA_TAG* target, + AMQP_VALUE_DATA_TAG* properties) + { + SessionImpl* session = static_cast(context); + _internal::LinkEndpoint linkEndpoint(newLinkEndpoint); + if (session->m_eventHandler) + { + return session->m_eventHandler->OnLinkAttached( + session->shared_from_this(), + linkEndpoint, + name, + role == role_receiver ? Azure::Core::Amqp::_internal::SessionRole::Receiver + : Azure::Core::Amqp::_internal::SessionRole::Sender, + source, + target, + properties); + } + else + { + // Even if we don't have any actions to take, if we return false, the connection will be + // aborted. + return true; + } + static_cast(role); + } + } // namespace _detail }}} // namespace Azure::Core::Amqp From 48f155fe0b3ed8f266cc8deb3c3ca231e18e05f9 Mon Sep 17 00:00:00 2001 From: Larry Osterman Date: Wed, 26 Apr 2023 17:07:14 -0700 Subject: [PATCH 03/17] clang fix 2 --- .../azure-core-amqp/test/ut/amqp_value_tests.cpp | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/sdk/core/azure-core-amqp/test/ut/amqp_value_tests.cpp b/sdk/core/azure-core-amqp/test/ut/amqp_value_tests.cpp index b360d7ab43..28952d8821 100644 --- a/sdk/core/azure-core-amqp/test/ut/amqp_value_tests.cpp +++ b/sdk/core/azure-core-amqp/test/ut/amqp_value_tests.cpp @@ -6,6 +6,7 @@ #include "azure/core/amqp/models/amqp_value.hpp" #include #include +#include using namespace Azure::Core::Amqp::Models; @@ -1160,7 +1161,7 @@ TEST_F(TestValueSerialization, SerializeList) constexpr size_t valueCount = 0x15; // Create an array of random values. std::vector values; - for (int i = 0; i < valueCount; i += 1) + for (size_t i = 0; i < valueCount; i += 1) { values.push_back(AmqpValue(static_cast(GenerateRandomValue()))); } @@ -1196,7 +1197,7 @@ TEST_F(TestValueSerialization, SerializeList) constexpr size_t valueCount = 0x10; // Create an array of random values. std::vector values; - for (int i = 0; i < valueCount; i += 1) + for (size_t i = 0; i < valueCount; i += 1) { values.push_back(AmqpValue(static_cast(GenerateRandomValue()))); } @@ -1237,7 +1238,7 @@ TEST_F(TestValueSerialization, SerializeList) constexpr size_t valueCount = 0x210; // Create an array of random values. std::vector values; - for (int i = 0; i < valueCount; i += 1) + for (size_t i = 0; i < valueCount; i += 1) { values.push_back(AmqpValue(static_cast(GenerateRandomValue()))); } @@ -1308,7 +1309,7 @@ TEST_F(TestValueSerialization, SerializeArray) constexpr size_t valueCount = 0x10; // Create an array of random values. std::vector values; - for (int i = 0; i < valueCount; i += 1) + for (size_t i = 0; i < valueCount; i += 1) { values.push_back(AmqpValue(static_cast(GenerateRandomValue()))); } @@ -1353,7 +1354,7 @@ TEST_F(TestValueSerialization, SerializeArray) constexpr size_t valueCount = 0x210; // Create an array of random values. std::vector values; - for (int i = 0; i < valueCount; i += 1) + for (size_t i = 0; i < valueCount; i += 1) { values.push_back(AmqpValue(static_cast(GenerateRandomValue()))); } @@ -1436,7 +1437,7 @@ TEST_F(TestValueSerialization, SerializeMap) constexpr size_t valueCount = 0x10; // Create an map of random values. std::map values; - for (int i = 0; i < valueCount; i += 1) + for (size_t i = 0; i < valueCount; i += 1) { values.emplace( AmqpValue(std::to_string(i)), @@ -1486,7 +1487,7 @@ TEST_F(TestValueSerialization, SerializeMap) constexpr size_t valueCount = 0x210; // Create an map of random values. std::map values; - for (int i = 0; i < valueCount; i += 1) + for (size_t i = 0; i < valueCount; i += 1) { values.emplace( AmqpValue(std::to_string(i)), From b847ad6b05d79e2aa259d1d60891558e9a1f025e Mon Sep 17 00:00:00 2001 From: Larry Osterman Date: Wed, 26 Apr 2023 17:29:59 -0700 Subject: [PATCH 04/17] clang-format --- .../azure/core/amqp/models/amqp_header.hpp | 24 +- .../azure/core/amqp/models/amqp_message.hpp | 26 +- .../core/amqp/models/amqp_properties.hpp | 24 +- sdk/core/azure-core-amqp/src/amqp/session.cpp | 356 ++--- .../src/models/amqp_message.cpp | 1149 ++++++++--------- .../test/ut/amqp_value_tests.cpp | 6 +- .../test/ut/mock_amqp_server.hpp | 2 +- 7 files changed, 789 insertions(+), 798 deletions(-) diff --git a/sdk/core/azure-core-amqp/inc/azure/core/amqp/models/amqp_header.hpp b/sdk/core/azure-core-amqp/inc/azure/core/amqp/models/amqp_header.hpp index 0bf71df352..e52488b409 100644 --- a/sdk/core/azure-core-amqp/inc/azure/core/amqp/models/amqp_header.hpp +++ b/sdk/core/azure-core-amqp/inc/azure/core/amqp/models/amqp_header.hpp @@ -79,17 +79,17 @@ namespace Azure { namespace Core { namespace Amqp { namespace Models { }}}} // namespace Azure::Core::Amqp::Models namespace Azure { namespace Core { namespace Amqp { namespace Models { namespace _internal { - /** - * @brief uAMQP interoperability functions to convert a MessageHeader to a uAMQP HEADER_HANDLE - * and back. - * - * @remarks This class should not be used directly. It is used by the uAMQP interoperability - * layer. - */ - struct MessageHeaderFactory - { - static MessageHeader FromUamqp(UniqueMessageHeaderHandle const& properties); - static UniqueMessageHeaderHandle ToUamqp(MessageHeader const& properties); - }; + /** + * @brief uAMQP interoperability functions to convert a MessageHeader to a uAMQP HEADER_HANDLE + * and back. + * + * @remarks This class should not be used directly. It is used by the uAMQP interoperability + * layer. + */ + struct MessageHeaderFactory + { + static MessageHeader FromUamqp(UniqueMessageHeaderHandle const& properties); + static UniqueMessageHeaderHandle ToUamqp(MessageHeader const& properties); + }; }}}}} // namespace Azure::Core::Amqp::Models::_internal diff --git a/sdk/core/azure-core-amqp/inc/azure/core/amqp/models/amqp_message.hpp b/sdk/core/azure-core-amqp/inc/azure/core/amqp/models/amqp_message.hpp index e7d3bf7ee7..c20d062502 100644 --- a/sdk/core/azure-core-amqp/inc/azure/core/amqp/models/amqp_message.hpp +++ b/sdk/core/azure-core-amqp/inc/azure/core/amqp/models/amqp_message.hpp @@ -262,17 +262,17 @@ namespace Azure { namespace Core { namespace Amqp { namespace Models { }}}} // namespace Azure::Core::Amqp::Models namespace Azure { namespace Core { namespace Amqp { namespace Models { namespace _internal { - /** - * @brief uAMQP interoperability functions to convert a MessageProperties to a uAMQP - * PROPERTIES_HANDLE and back. - * - * @remarks This class should not be used directly. It is used by the uAMQP interoperability - * layer. - */ - class AmqpMessageFactory { - public: - static AmqpMessage FromUamqp(UniqueMessageHandle const& properties); - static AmqpMessage FromUamqp(MESSAGE_INSTANCE_TAG* properties); - static UniqueMessageHandle ToUamqp(AmqpMessage const& properties); - }; + /** + * @brief uAMQP interoperability functions to convert a MessageProperties to a uAMQP + * PROPERTIES_HANDLE and back. + * + * @remarks This class should not be used directly. It is used by the uAMQP interoperability + * layer. + */ + class AmqpMessageFactory { + public: + static AmqpMessage FromUamqp(UniqueMessageHandle const& properties); + static AmqpMessage FromUamqp(MESSAGE_INSTANCE_TAG* properties); + static UniqueMessageHandle ToUamqp(AmqpMessage const& properties); + }; }}}}} // namespace Azure::Core::Amqp::Models::_internal diff --git a/sdk/core/azure-core-amqp/inc/azure/core/amqp/models/amqp_properties.hpp b/sdk/core/azure-core-amqp/inc/azure/core/amqp/models/amqp_properties.hpp index 72ce629a4c..4bae491306 100644 --- a/sdk/core/azure-core-amqp/inc/azure/core/amqp/models/amqp_properties.hpp +++ b/sdk/core/azure-core-amqp/inc/azure/core/amqp/models/amqp_properties.hpp @@ -52,16 +52,16 @@ namespace Azure { namespace Core { namespace Amqp { namespace Models { }}}} // namespace Azure::Core::Amqp::Models namespace Azure { namespace Core { namespace Amqp { namespace Models { namespace _internal { - /** - * @brief uAMQP interoperability functions to convert a MessageProperties to a uAMQP - * PROPERTIES_HANDLE and back. - * - * @remarks This class should not be used directly. It is used by the uAMQP interoperability - * layer. - */ - class MessagePropertiesFactory { - public: - static MessageProperties FromUamqp(UniquePropertiesHandle const& properties); - static UniquePropertiesHandle ToUamqp(MessageProperties const& properties); - }; + /** + * @brief uAMQP interoperability functions to convert a MessageProperties to a uAMQP + * PROPERTIES_HANDLE and back. + * + * @remarks This class should not be used directly. It is used by the uAMQP interoperability + * layer. + */ + class MessagePropertiesFactory { + public: + static MessageProperties FromUamqp(UniquePropertiesHandle const& properties); + static UniquePropertiesHandle ToUamqp(MessageProperties const& properties); + }; }}}}} // namespace Azure::Core::Amqp::Models::_internal diff --git a/sdk/core/azure-core-amqp/src/amqp/session.cpp b/sdk/core/azure-core-amqp/src/amqp/session.cpp index f409517ee4..c7d906e9de 100644 --- a/sdk/core/azure-core-amqp/src/amqp/session.cpp +++ b/sdk/core/azure-core-amqp/src/amqp/session.cpp @@ -16,184 +16,184 @@ void Azure::Core::_internal::UniqueHandleHelper::FreeAmqpS } namespace Azure { namespace Core { namespace Amqp { - namespace _internal { - Endpoint::~Endpoint() - { - if (m_endpoint) - { - connection_destroy_endpoint(m_endpoint); - } - } - - Session::Session( - Connection const& parentConnection, - Endpoint& newEndpoint, - SessionEvents* eventHandler) - : m_impl{std::make_shared( - parentConnection.GetImpl(), - newEndpoint, - eventHandler)} - { - } - - Session::Session(Connection const& parentConnection, SessionEvents* eventHandler) - : m_impl{std::make_shared( - parentConnection.GetImpl(), - eventHandler)} - { - } - - Session::~Session() noexcept {} - - void Session::SetIncomingWindow(uint32_t incomingWindow) - { - m_impl->SetIncomingWindow(incomingWindow); + namespace _internal { + Endpoint::~Endpoint() + { + if (m_endpoint) + { + connection_destroy_endpoint(m_endpoint); + } + } + + Session::Session( + Connection const& parentConnection, + Endpoint& newEndpoint, + SessionEvents* eventHandler) + : m_impl{std::make_shared( + parentConnection.GetImpl(), + newEndpoint, + eventHandler)} + { + } + + Session::Session(Connection const& parentConnection, SessionEvents* eventHandler) + : m_impl{std::make_shared( + parentConnection.GetImpl(), + eventHandler)} + { + } + + Session::~Session() noexcept {} + + void Session::SetIncomingWindow(uint32_t incomingWindow) + { + m_impl->SetIncomingWindow(incomingWindow); + } + uint32_t Session::GetIncomingWindow() const { return m_impl->GetIncomingWindow(); } + void Session::SetOutgoingWindow(uint32_t outgoingWindow) + { + m_impl->SetOutgoingWindow(outgoingWindow); + } + uint32_t Session::GetOutgoingWindow() const { return m_impl->GetOutgoingWindow(); } + void Session::SetHandleMax(uint32_t handleMax) { m_impl->SetHandleMax(handleMax); } + uint32_t Session::GetHandleMax() const { return m_impl->GetHandleMax(); } + + void Session::Begin() { m_impl->Begin(); } + void Session::End(std::string const& condition_value, std::string const& description) + { + m_impl->End(condition_value, description); + } + } // namespace _internal + + namespace _detail { + + SessionImpl::~SessionImpl() noexcept {} + + SessionImpl::SessionImpl( + std::shared_ptr<_detail::ConnectionImpl> connection, + _internal::Endpoint& endpoint, + _internal::SessionEvents* eventHandler) + : m_connectionToPoll(connection), m_session{session_create_from_endpoint( + *connection, + endpoint.Release(), + SessionImpl::OnLinkAttachedFn, + this)}, + m_eventHandler{eventHandler} + { + } + + SessionImpl::SessionImpl( + std::shared_ptr<_detail::ConnectionImpl> connection, + _internal::SessionEvents* eventHandler) + : m_connectionToPoll(connection), + m_session{session_create(*connection, SessionImpl::OnLinkAttachedFn, this)}, + m_eventHandler{eventHandler} + + { + } + + void SessionImpl::SetIncomingWindow(uint32_t window) + { + if (session_set_incoming_window(m_session.get(), window)) + { + throw std::runtime_error("Could not set incoming window"); // LCOV_EXCL_LINE + } + } + uint32_t SessionImpl::GetIncomingWindow() + { + uint32_t window; + if (session_get_incoming_window(m_session.get(), &window)) + { + throw std::runtime_error("Could not get incoming window"); // LCOV_EXCL_LINE + } + return window; + } + + void SessionImpl::SetOutgoingWindow(uint32_t window) + { + if (session_set_outgoing_window(m_session.get(), window)) + { + throw std::runtime_error("Could not set outgoing window"); // LCOV_EXCL_LINE + } + } + + uint32_t SessionImpl::GetOutgoingWindow() + { + uint32_t window; + if (session_get_outgoing_window(m_session.get(), &window)) + { + throw std::runtime_error("Could not get outgoing window"); // LCOV_EXCL_LINE + } + return window; + } + + void SessionImpl::SetHandleMax(uint32_t max) + { + if (session_set_handle_max(m_session.get(), max)) + { + throw std::runtime_error("Could not set handle max."); // LCOV_EXCL_LINE + } + } + uint32_t SessionImpl::GetHandleMax() + { + uint32_t max; + if (session_get_handle_max(m_session.get(), &max)) + { + throw std::runtime_error("Could not get handle max."); // LCOV_EXCL_LINE + } + return max; + } + + void SessionImpl::Begin() + { + if (session_begin(m_session.get())) + { + throw std::runtime_error("Could not begin session"); // LCOV_EXCL_LINE + } + } + void SessionImpl::End(const std::string& condition, const std::string& description) + { + // When we end the session, it clears all the links, so we need to ensure that the + // m_newLinkAttachedQueue.Clear(); + if (session_end( + m_session.get(), + condition.empty() ? nullptr : condition.c_str(), + description.empty() ? nullptr : description.c_str())) + { + throw std::runtime_error("Could not begin session"); // LCOV_EXCL_LINE + } + } + + bool SessionImpl::OnLinkAttachedFn( + void* context, + LINK_ENDPOINT_INSTANCE_TAG* newLinkEndpoint, + const char* name, + bool role, + AMQP_VALUE_DATA_TAG* source, + AMQP_VALUE_DATA_TAG* target, + AMQP_VALUE_DATA_TAG* properties) + { + SessionImpl* session = static_cast(context); + _internal::LinkEndpoint linkEndpoint(newLinkEndpoint); + if (session->m_eventHandler) + { + return session->m_eventHandler->OnLinkAttached( + session->shared_from_this(), + linkEndpoint, + name, + role == role_receiver ? Azure::Core::Amqp::_internal::SessionRole::Receiver + : Azure::Core::Amqp::_internal::SessionRole::Sender, + source, + target, + properties); } - uint32_t Session::GetIncomingWindow() const { return m_impl->GetIncomingWindow(); } - void Session::SetOutgoingWindow(uint32_t outgoingWindow) - { - m_impl->SetOutgoingWindow(outgoingWindow); - } - uint32_t Session::GetOutgoingWindow() const { return m_impl->GetOutgoingWindow(); } - void Session::SetHandleMax(uint32_t handleMax) { m_impl->SetHandleMax(handleMax); } - uint32_t Session::GetHandleMax() const { return m_impl->GetHandleMax(); } - - void Session::Begin() { m_impl->Begin(); } - void Session::End(std::string const& condition_value, std::string const& description) - { - m_impl->End(condition_value, description); - } - } // namespace _internal - - namespace _detail { - - SessionImpl::~SessionImpl() noexcept {} - - SessionImpl::SessionImpl( - std::shared_ptr<_detail::ConnectionImpl> connection, - _internal::Endpoint& endpoint, - _internal::SessionEvents* eventHandler) - : m_connectionToPoll(connection), m_session{session_create_from_endpoint( - *connection, - endpoint.Release(), - SessionImpl::OnLinkAttachedFn, - this)}, - m_eventHandler{eventHandler} - { - } - - SessionImpl::SessionImpl( - std::shared_ptr<_detail::ConnectionImpl> connection, - _internal::SessionEvents* eventHandler) - : m_connectionToPoll(connection), - m_session{session_create(*connection, SessionImpl::OnLinkAttachedFn, this)}, - m_eventHandler{eventHandler} - - { - } - - void SessionImpl::SetIncomingWindow(uint32_t window) + else { - if (session_set_incoming_window(m_session.get(), window)) - { - throw std::runtime_error("Could not set incoming window"); // LCOV_EXCL_LINE - } - } - uint32_t SessionImpl::GetIncomingWindow() - { - uint32_t window; - if (session_get_incoming_window(m_session.get(), &window)) - { - throw std::runtime_error("Could not get incoming window"); // LCOV_EXCL_LINE - } - return window; - } - - void SessionImpl::SetOutgoingWindow(uint32_t window) - { - if (session_set_outgoing_window(m_session.get(), window)) - { - throw std::runtime_error("Could not set outgoing window"); // LCOV_EXCL_LINE - } - } - - uint32_t SessionImpl::GetOutgoingWindow() - { - uint32_t window; - if (session_get_outgoing_window(m_session.get(), &window)) - { - throw std::runtime_error("Could not get outgoing window"); // LCOV_EXCL_LINE - } - return window; - } - - void SessionImpl::SetHandleMax(uint32_t max) - { - if (session_set_handle_max(m_session.get(), max)) - { - throw std::runtime_error("Could not set handle max."); // LCOV_EXCL_LINE - } - } - uint32_t SessionImpl::GetHandleMax() - { - uint32_t max; - if (session_get_handle_max(m_session.get(), &max)) - { - throw std::runtime_error("Could not get handle max."); // LCOV_EXCL_LINE - } - return max; - } - - void SessionImpl::Begin() - { - if (session_begin(m_session.get())) - { - throw std::runtime_error("Could not begin session"); // LCOV_EXCL_LINE - } - } - void SessionImpl::End(const std::string& condition, const std::string& description) - { - // When we end the session, it clears all the links, so we need to ensure that the - // m_newLinkAttachedQueue.Clear(); - if (session_end( - m_session.get(), - condition.empty() ? nullptr : condition.c_str(), - description.empty() ? nullptr : description.c_str())) - { - throw std::runtime_error("Could not begin session"); // LCOV_EXCL_LINE - } - } - - bool SessionImpl::OnLinkAttachedFn( - void* context, - LINK_ENDPOINT_INSTANCE_TAG* newLinkEndpoint, - const char* name, - bool role, - AMQP_VALUE_DATA_TAG* source, - AMQP_VALUE_DATA_TAG* target, - AMQP_VALUE_DATA_TAG* properties) - { - SessionImpl* session = static_cast(context); - _internal::LinkEndpoint linkEndpoint(newLinkEndpoint); - if (session->m_eventHandler) - { - return session->m_eventHandler->OnLinkAttached( - session->shared_from_this(), - linkEndpoint, - name, - role == role_receiver ? Azure::Core::Amqp::_internal::SessionRole::Receiver - : Azure::Core::Amqp::_internal::SessionRole::Sender, - source, - target, - properties); - } - else - { - // Even if we don't have any actions to take, if we return false, the connection will be - // aborted. - return true; - } - static_cast(role); - } - } // namespace _detail + // Even if we don't have any actions to take, if we return false, the connection will be + // aborted. + return true; + } + static_cast(role); + } + } // namespace _detail }}} // namespace Azure::Core::Amqp diff --git a/sdk/core/azure-core-amqp/src/models/amqp_message.cpp b/sdk/core/azure-core-amqp/src/models/amqp_message.cpp index 061bd9a48f..6cb6653590 100644 --- a/sdk/core/azure-core-amqp/src/models/amqp_message.cpp +++ b/sdk/core/azure-core-amqp/src/models/amqp_message.cpp @@ -23,741 +23,732 @@ using namespace Azure::Core::Amqp::_detail; namespace Azure { namespace Core { namespace Amqp { namespace Models { - namespace { + namespace { - UniqueMessageHeaderHandle GetHeaderFromMessage(MESSAGE_HANDLE message) + UniqueMessageHeaderHandle GetHeaderFromMessage(MESSAGE_HANDLE message) + { + if (message != nullptr) { - if (message != nullptr) + HEADER_HANDLE headerValue; + if (!message_get_header(message, &headerValue)) { - HEADER_HANDLE headerValue; - if (!message_get_header(message, &headerValue)) - { - return Azure::Core::_internal::UniqueHandle(headerValue); - } + return Azure::Core::_internal::UniqueHandle(headerValue); } - return nullptr; } + return nullptr; + } - UniquePropertiesHandle GetPropertiesFromMessage(MESSAGE_HANDLE const& message) + UniquePropertiesHandle GetPropertiesFromMessage(MESSAGE_HANDLE const& message) + { + if (message != nullptr) { - if (message != nullptr) + PROPERTIES_HANDLE propertiesValue; + if (!message_get_properties(message, &propertiesValue)) { - PROPERTIES_HANDLE propertiesValue; - if (!message_get_properties(message, &propertiesValue)) - { - return Azure::Core::_internal::UniqueHandle(propertiesValue); - } + return Azure::Core::_internal::UniqueHandle(propertiesValue); } - return nullptr; } - } // namespace + return nullptr; + } + } // namespace - AmqpMessage _internal::AmqpMessageFactory::FromUamqp(UniqueMessageHandle const& message) + AmqpMessage _internal::AmqpMessageFactory::FromUamqp(UniqueMessageHandle const& message) + { + return FromUamqp(message.get()); + } + + AmqpMessage _internal::AmqpMessageFactory::FromUamqp(MESSAGE_HANDLE message) + { + if (message == nullptr) { - return FromUamqp(message.get()); + return AmqpMessage(nullptr); } + AmqpMessage rv; + rv.Header = _internal::MessageHeaderFactory::FromUamqp(GetHeaderFromMessage(message)); + rv.Properties + = _internal::MessagePropertiesFactory::FromUamqp(GetPropertiesFromMessage(message)); - AmqpMessage _internal::AmqpMessageFactory::FromUamqp(MESSAGE_HANDLE message) { - if (message == nullptr) + delivery_annotations annotationsVal; + // message_get_delivery_annotations returns a clone of the message annotations. + if (!message_get_delivery_annotations(message, &annotationsVal) && annotationsVal != nullptr) { - return AmqpMessage(nullptr); - } - AmqpMessage rv; - rv.Header = _internal::MessageHeaderFactory::FromUamqp(GetHeaderFromMessage(message)); - rv.Properties - = _internal::MessagePropertiesFactory::FromUamqp(GetPropertiesFromMessage(message)); - - { - delivery_annotations annotationsVal; - // message_get_delivery_annotations returns a clone of the message annotations. - if (!message_get_delivery_annotations(message, &annotationsVal) - && annotationsVal != nullptr) - { - UniqueAmqpValueHandle deliveryAnnotations(annotationsVal); - auto deliveryMap = AmqpValue{deliveryAnnotations.get()}.AsMap(); - rv.DeliveryAnnotations = deliveryMap; - } + UniqueAmqpValueHandle deliveryAnnotations(annotationsVal); + auto deliveryMap = AmqpValue{deliveryAnnotations.get()}.AsMap(); + rv.DeliveryAnnotations = deliveryMap; } + } + { + // message_get_message_annotations returns a clone of the message annotations. + AMQP_VALUE annotationVal; + if (!message_get_message_annotations(message, &annotationVal) && annotationVal) { - // message_get_message_annotations returns a clone of the message annotations. - AMQP_VALUE annotationVal; - if (!message_get_message_annotations(message, &annotationVal) && annotationVal) + UniqueAmqpValueHandle messageAnnotations(annotationVal); + if (messageAnnotations) { - UniqueAmqpValueHandle messageAnnotations(annotationVal); - if (messageAnnotations) - { - auto messageMap = AmqpValue{messageAnnotations.get()}.AsMap(); - rv.MessageAnnotations = messageMap; - } + auto messageMap = AmqpValue{messageAnnotations.get()}.AsMap(); + rv.MessageAnnotations = messageMap; } } - { - /* - * The ApplicationProperties field in an AMQP message for uAMQP expects that the map value - * is wrapped as a described value. A described value has a ULONG descriptor value and a - * value type. - * - * Making things even more interesting, the ApplicationProperties field in an uAMQP message - * is asymmetric. - * - * The MessageSender class will wrap ApplicationProperties in a described value, so when - * setting application properties, the described value must NOT be present, but when - * decoding an application properties, the GetApplicationProperties method has to be able to - * handle both when the described value is present or not. - */ - AMQP_VALUE properties; - if (!message_get_application_properties(message, &properties) && properties) + } + { + /* + * The ApplicationProperties field in an AMQP message for uAMQP expects that the map value + * is wrapped as a described value. A described value has a ULONG descriptor value and a + * value type. + * + * Making things even more interesting, the ApplicationProperties field in an uAMQP message + * is asymmetric. + * + * The MessageSender class will wrap ApplicationProperties in a described value, so when + * setting application properties, the described value must NOT be present, but when + * decoding an application properties, the GetApplicationProperties method has to be able to + * handle both when the described value is present or not. + */ + AMQP_VALUE properties; + if (!message_get_application_properties(message, &properties) && properties) + { + UniqueAmqpValueHandle describedProperties(properties); + properties = nullptr; + if (describedProperties) { - UniqueAmqpValueHandle describedProperties(properties); - properties = nullptr; - if (describedProperties) + AMQP_VALUE value; + if (amqpvalue_get_type(describedProperties.get()) == AMQP_TYPE_DESCRIBED) { - AMQP_VALUE value; - if (amqpvalue_get_type(describedProperties.get()) == AMQP_TYPE_DESCRIBED) + auto describedType = amqpvalue_get_inplace_descriptor(describedProperties.get()); + uint64_t describedTypeValue; + if (amqpvalue_get_ulong(describedType, &describedTypeValue)) { - auto describedType = amqpvalue_get_inplace_descriptor(describedProperties.get()); - uint64_t describedTypeValue; - if (amqpvalue_get_ulong(describedType, &describedTypeValue)) - { - throw std::runtime_error( - "Could not retrieve application properties described type."); - } - if (describedTypeValue - != static_cast( - Azure::Core::Amqp::_detail::AmqpDescriptors::ApplicationProperties)) - { - throw std::runtime_error( - "Application Properties are not the corect described type."); - } - - value = amqpvalue_get_inplace_described_value(describedProperties.get()); + throw std::runtime_error("Could not retrieve application properties described type."); } - else + if (describedTypeValue + != static_cast( + Azure::Core::Amqp::_detail::AmqpDescriptors::ApplicationProperties)) { - value = describedProperties.get(); + throw std::runtime_error("Application Properties are not the corect described type."); } - if (amqpvalue_get_type(value) != AMQP_TYPE_MAP) - { - throw std::runtime_error("Application Properties must be a map?!"); - } - auto appProperties = AmqpMap(value); - for (auto const& val : appProperties) + + value = amqpvalue_get_inplace_described_value(describedProperties.get()); + } + else + { + value = describedProperties.get(); + } + if (amqpvalue_get_type(value) != AMQP_TYPE_MAP) + { + throw std::runtime_error("Application Properties must be a map?!"); + } + auto appProperties = AmqpMap(value); + for (auto const& val : appProperties) + { + if (val.first.GetType() != AmqpValueType::String) { - if (val.first.GetType() != AmqpValueType::String) - { - throw std::runtime_error("Key of Application Properties must be a string."); - } - rv.ApplicationProperties.emplace( - std::make_pair(static_cast(val.first), val.second)); + throw std::runtime_error("Key of Application Properties must be a string."); } + rv.ApplicationProperties.emplace( + std::make_pair(static_cast(val.first), val.second)); } } } + } + { + annotations footerVal; + if (!message_get_footer(message, &footerVal) && footerVal) { - annotations footerVal; - if (!message_get_footer(message, &footerVal) && footerVal) - { - UniqueAmqpValueHandle footerAnnotations(footerVal); - footerVal = nullptr; - auto footerMap = AmqpValue{footerAnnotations.get()}.AsMap(); - rv.Footer = footerMap; - } + UniqueAmqpValueHandle footerAnnotations(footerVal); + footerVal = nullptr; + auto footerMap = AmqpValue{footerAnnotations.get()}.AsMap(); + rv.Footer = footerMap; } - { - MESSAGE_BODY_TYPE bodyType; + } + { + MESSAGE_BODY_TYPE bodyType; - if (!message_get_body_type(message, &bodyType)) + if (!message_get_body_type(message, &bodyType)) + { + switch (bodyType) { - switch (bodyType) - { - case MESSAGE_BODY_TYPE_NONE: - rv.BodyType = MessageBodyType::None; - break; - case MESSAGE_BODY_TYPE_DATA: { - size_t dataCount; - if (!message_get_body_amqp_data_count(message, &dataCount)) + case MESSAGE_BODY_TYPE_NONE: + rv.BodyType = MessageBodyType::None; + break; + case MESSAGE_BODY_TYPE_DATA: { + size_t dataCount; + if (!message_get_body_amqp_data_count(message, &dataCount)) + { + for (auto i = 0ul; i < dataCount; i += 1) { - for (auto i = 0ul; i < dataCount; i += 1) + BINARY_DATA binaryValue; + if (!message_get_body_amqp_data_in_place(message, i, &binaryValue)) { - BINARY_DATA binaryValue; - if (!message_get_body_amqp_data_in_place(message, i, &binaryValue)) - { - rv.m_binaryDataBody.push_back(AmqpBinaryData(std::vector( - binaryValue.bytes, binaryValue.bytes + binaryValue.length))); - } + rv.m_binaryDataBody.push_back(AmqpBinaryData(std::vector( + binaryValue.bytes, binaryValue.bytes + binaryValue.length))); } } - rv.BodyType = MessageBodyType::Data; } - break; - case MESSAGE_BODY_TYPE_SEQUENCE: { + rv.BodyType = MessageBodyType::Data; + } + break; + case MESSAGE_BODY_TYPE_SEQUENCE: { - size_t sequenceCount; - if (!message_get_body_amqp_sequence_count(message, &sequenceCount)) + size_t sequenceCount; + if (!message_get_body_amqp_sequence_count(message, &sequenceCount)) + { + for (auto i = 0ul; i < sequenceCount; i += 1) { - for (auto i = 0ul; i < sequenceCount; i += 1) + AMQP_VALUE sequence; + if (!message_get_body_amqp_sequence_in_place(message, i, &sequence)) { - AMQP_VALUE sequence; - if (!message_get_body_amqp_sequence_in_place(message, i, &sequence)) - { - rv.m_amqpSequenceBody.push_back(sequence); - } + rv.m_amqpSequenceBody.push_back(sequence); } } - rv.BodyType = MessageBodyType::Sequence; } - break; - case MESSAGE_BODY_TYPE_VALUE: { - AMQP_VALUE bodyValue; - if (!message_get_body_amqp_value_in_place(message, &bodyValue)) - { - rv.m_amqpValueBody = bodyValue; - } - rv.BodyType = MessageBodyType::Value; + rv.BodyType = MessageBodyType::Sequence; + } + break; + case MESSAGE_BODY_TYPE_VALUE: { + AMQP_VALUE bodyValue; + if (!message_get_body_amqp_value_in_place(message, &bodyValue)) + { + rv.m_amqpValueBody = bodyValue; } - break; - case MESSAGE_BODY_TYPE_INVALID: - default: - throw std::runtime_error("Unknown body type."); + rv.BodyType = MessageBodyType::Value; } + break; + case MESSAGE_BODY_TYPE_INVALID: + default: + throw std::runtime_error("Unknown body type."); } } - return rv; } + return rv; + } - UniqueMessageHandle _internal::AmqpMessageFactory::ToUamqp(AmqpMessage const& message) + UniqueMessageHandle _internal::AmqpMessageFactory::ToUamqp(AmqpMessage const& message) + { + UniqueMessageHandle rv(message_create()); + + // AMQP 1.0 specifies a message format of 0. + if (message_set_message_format(rv.get(), AmqpMessageFormatValue)) { - UniqueMessageHandle rv(message_create()); + throw std::runtime_error("Could not set destination message format."); + } - // AMQP 1.0 specifies a message format of 0. - if (message_set_message_format(rv.get(), AmqpMessageFormatValue)) - { - throw std::runtime_error("Could not set destination message format."); - } + if (message_set_header( + rv.get(), _internal::MessageHeaderFactory::ToUamqp(message.Header).get())) + { + throw std::runtime_error("Could not set message header."); + } + if (message_set_properties( + rv.get(), _internal::MessagePropertiesFactory::ToUamqp(message.Properties).get())) + { + throw std::runtime_error("Could not set message properties."); + } - if (message_set_header( - rv.get(), _internal::MessageHeaderFactory::ToUamqp(message.Header).get())) + if (!message.DeliveryAnnotations.empty()) + { + if (message_set_delivery_annotations( + rv.get(), static_cast(message.DeliveryAnnotations).get())) { - throw std::runtime_error("Could not set message header."); + throw std::runtime_error("Could not set delivery annotations."); } - if (message_set_properties( - rv.get(), _internal::MessagePropertiesFactory::ToUamqp(message.Properties).get())) + } + if (!message.MessageAnnotations.empty()) + { + if (message_set_message_annotations( + rv.get(), static_cast(message.MessageAnnotations).get())) { - throw std::runtime_error("Could not set message properties."); + throw std::runtime_error("Could not set message annotations."); } - - if (!message.DeliveryAnnotations.empty()) + } + if (!message.ApplicationProperties.empty()) + { + AmqpMap appProperties; + for (auto const& val : message.ApplicationProperties) { - if (message_set_delivery_annotations( - rv.get(), static_cast(message.DeliveryAnnotations).get())) + if ((val.second.GetType() == AmqpValueType::List) + || (val.second.GetType() == AmqpValueType::Map) + || (val.second.GetType() == AmqpValueType::Composite) + || (val.second.GetType() == AmqpValueType::Described)) { - throw std::runtime_error("Could not set delivery annotations."); + throw std::runtime_error( + "Message Application Property values must be simple value types"); } + appProperties.emplace(val); } - if (!message.MessageAnnotations.empty()) + if (message_set_application_properties( + rv.get(), static_cast(appProperties).get())) { - if (message_set_message_annotations( - rv.get(), static_cast(message.MessageAnnotations).get())) - { - throw std::runtime_error("Could not set message annotations."); - } + throw std::runtime_error("Could not set application properties."); } - if (!message.ApplicationProperties.empty()) + } + if (!message.Footer.empty()) + { + if (message_set_footer(rv.get(), static_cast(message.Footer).get())) { - AmqpMap appProperties; - for (auto const& val : message.ApplicationProperties) + throw std::runtime_error("Could not set message annotations."); + } + } + switch (message.BodyType) + { + case MessageBodyType::None: + break; + case MessageBodyType::Data: + for (auto const& binaryVal : message.m_binaryDataBody) { - if ((val.second.GetType() == AmqpValueType::List) - || (val.second.GetType() == AmqpValueType::Map) - || (val.second.GetType() == AmqpValueType::Composite) - || (val.second.GetType() == AmqpValueType::Described)) + BINARY_DATA valueData; + valueData.bytes = binaryVal.data(); + valueData.length = static_cast(binaryVal.size()); + if (message_add_body_amqp_data(rv.get(), valueData)) { - throw std::runtime_error( - "Message Application Property values must be simple value types"); + throw std::runtime_error("Could not set message body AMQP sequence value."); } - appProperties.emplace(val); } - if (message_set_application_properties( - rv.get(), static_cast(appProperties).get())) + break; + case MessageBodyType::Sequence: + for (auto const& sequenceVal : message.m_amqpSequenceBody) { - throw std::runtime_error("Could not set application properties."); + if (message_add_body_amqp_sequence(rv.get(), AmqpValue(sequenceVal))) + { + throw std::runtime_error("Could not set message body AMQP sequence value."); + } } - } - if (!message.Footer.empty()) - { - if (message_set_footer(rv.get(), static_cast(message.Footer).get())) + break; + case MessageBodyType::Value: + if (message_set_body_amqp_value(rv.get(), message.m_amqpValueBody)) { - throw std::runtime_error("Could not set message annotations."); + throw std::runtime_error("Could not set message body AMQP value."); } - } - switch (message.BodyType) - { - case MessageBodyType::None: - break; - case MessageBodyType::Data: - for (auto const& binaryVal : message.m_binaryDataBody) - { - BINARY_DATA valueData; - valueData.bytes = binaryVal.data(); - valueData.length = static_cast(binaryVal.size()); - if (message_add_body_amqp_data(rv.get(), valueData)) - { - throw std::runtime_error("Could not set message body AMQP sequence value."); - } - } - break; - case MessageBodyType::Sequence: - for (auto const& sequenceVal : message.m_amqpSequenceBody) - { - if (message_add_body_amqp_sequence(rv.get(), AmqpValue(sequenceVal))) - { - throw std::runtime_error("Could not set message body AMQP sequence value."); - } - } - break; - case MessageBodyType::Value: - if (message_set_body_amqp_value(rv.get(), message.m_amqpValueBody)) - { - throw std::runtime_error("Could not set message body AMQP value."); - } - break; - case MessageBodyType::Invalid: // LCOV_EXCL_LINE - default: // LCOV_EXCL_LINE - throw std::runtime_error("Unknown message body type."); // LCOV_EXCL_LINE - } - return rv; + break; + case MessageBodyType::Invalid: // LCOV_EXCL_LINE + default: // LCOV_EXCL_LINE + throw std::runtime_error("Unknown message body type."); // LCOV_EXCL_LINE } + return rv; + } - std::vector AmqpMessage::GetBodyAsAmqpList() const + std::vector AmqpMessage::GetBodyAsAmqpList() const + { + if (BodyType != MessageBodyType::Sequence) { - if (BodyType != MessageBodyType::Sequence) - { - throw std::runtime_error("Invalid body type, should be MessageBodyType::Sequence."); - } - return m_amqpSequenceBody; + throw std::runtime_error("Invalid body type, should be MessageBodyType::Sequence."); } + return m_amqpSequenceBody; + } - void AmqpMessage::SetBody(AmqpBinaryData const& value) + void AmqpMessage::SetBody(AmqpBinaryData const& value) + { + BodyType = MessageBodyType::Data; + m_binaryDataBody.push_back(value); + } + void AmqpMessage::SetBody(std::vector const& value) + { + BodyType = MessageBodyType::Data; + m_binaryDataBody = value; + } + void AmqpMessage::SetBody(AmqpValue const& value) + { + BodyType = MessageBodyType::Value; + m_amqpValueBody = value; + } + void AmqpMessage::SetBody(std::vector const& value) + { + BodyType = MessageBodyType::Sequence; + m_amqpSequenceBody = value; + } + void AmqpMessage::SetBody(AmqpList const& value) + { + BodyType = MessageBodyType::Sequence; + m_amqpSequenceBody.push_back(value); + } + + AmqpValue AmqpMessage::GetBodyAsAmqpValue() const + { + if (BodyType != MessageBodyType::Value) { - BodyType = MessageBodyType::Data; - m_binaryDataBody.push_back(value); + throw std::runtime_error("Invalid body type, should be MessageBodyType::Value."); } - void AmqpMessage::SetBody(std::vector const& value) + return m_amqpValueBody; + } + std::vector AmqpMessage::GetBodyAsBinary() const + { + if (BodyType != MessageBodyType::Data) { - BodyType = MessageBodyType::Data; - m_binaryDataBody = value; + throw std::runtime_error("Invalid body type, should be MessageBodyType::Value."); } - void AmqpMessage::SetBody(AmqpValue const& value) + return m_binaryDataBody; + } + + bool AmqpMessage::operator==(AmqpMessage const& that) const noexcept + { + return (Header == that.Header) && (DeliveryAnnotations == that.DeliveryAnnotations) + && (MessageAnnotations == that.MessageAnnotations) && (Properties == that.Properties) + && (ApplicationProperties == that.ApplicationProperties) && (Footer == that.Footer) + && (BodyType == that.BodyType) && (m_amqpValueBody == that.m_amqpValueBody) + && (m_amqpSequenceBody == that.m_amqpSequenceBody) + && (m_binaryDataBody == that.m_binaryDataBody); + } + + size_t AmqpMessage::GetSerializedSize(AmqpMessage const& message) + { + size_t serializedSize{}; + + serializedSize += MessageHeader::GetSerializedSize(message.Header); + serializedSize += AmqpValue::GetSerializedSize(message.MessageAnnotations); + serializedSize += MessageProperties::GetSerializedSize(message.Properties); + + // ApplicationProperties is a map of string to value, we need to convert it to an AmqpMap. { - BodyType = MessageBodyType::Value; - m_amqpValueBody = value; + AmqpMap appProperties; + for (auto const& val : message.ApplicationProperties) + { + if ((val.second.GetType() == AmqpValueType::List) + || (val.second.GetType() == AmqpValueType::Map) + || (val.second.GetType() == AmqpValueType::Composite) + || (val.second.GetType() == AmqpValueType::Described)) + { + throw std::runtime_error( + "Message Application Property values must be simple value types"); + } + appProperties.emplace(val); + } + serializedSize += AmqpValue::GetSerializedSize(appProperties); } - void AmqpMessage::SetBody(std::vector const& value) + + switch (message.BodyType) { - BodyType = MessageBodyType::Sequence; - m_amqpSequenceBody = value; + default: + case MessageBodyType::Invalid: + throw std::runtime_error("Invalid message body type."); + + case MessageBodyType::Value: + serializedSize += AmqpValue::GetSerializedSize(message.m_amqpValueBody); + break; + case MessageBodyType::Data: + for (auto const& val : message.m_binaryDataBody) + { + serializedSize += AmqpValue::GetSerializedSize(val); + } + break; + case MessageBodyType::Sequence: + for (auto const& val : message.m_amqpSequenceBody) + { + serializedSize += AmqpValue::GetSerializedSize(val); + } + break; } - void AmqpMessage::SetBody(AmqpList const& value) + return serializedSize; + } + + std::vector AmqpMessage::Serialize(AmqpMessage const& message) + { + // size_t serializedSize = AmqpMessage::GetSerializedSize(message); + + std::vector rv; + + // Append the message Header to the serialized message. + if (message.Header.ShouldSerialize()) { - BodyType = MessageBodyType::Sequence; - m_amqpSequenceBody.push_back(value); + auto serializedHeader = MessageHeader::Serialize(message.Header); + rv.insert(rv.end(), serializedHeader.begin(), serializedHeader.end()); } - - AmqpValue AmqpMessage::GetBodyAsAmqpValue() const + if (!message.DeliveryAnnotations.empty()) { - if (BodyType != MessageBodyType::Value) - { - throw std::runtime_error("Invalid body type, should be MessageBodyType::Value."); - } - return m_amqpValueBody; + AmqpValue deliveryAnnotations{ + amqpvalue_create_delivery_annotations(AmqpValue{message.DeliveryAnnotations})}; + auto serializedDeliveryAnnotations = AmqpValue::Serialize(deliveryAnnotations); + rv.insert( + rv.end(), serializedDeliveryAnnotations.begin(), serializedDeliveryAnnotations.end()); } - std::vector AmqpMessage::GetBodyAsBinary() const + if (!message.MessageAnnotations.empty()) { - if (BodyType != MessageBodyType::Data) - { - throw std::runtime_error("Invalid body type, should be MessageBodyType::Value."); - } - return m_binaryDataBody; + AmqpValue messageAnnotations{ + amqpvalue_create_message_annotations(AmqpValue{message.MessageAnnotations})}; + auto serializedAnnotations = AmqpValue::Serialize(messageAnnotations); + rv.insert(rv.end(), serializedAnnotations.begin(), serializedAnnotations.end()); } - bool AmqpMessage::operator==(AmqpMessage const& that) const noexcept + if (message.Properties.ShouldSerialize()) { - return (Header == that.Header) && (DeliveryAnnotations == that.DeliveryAnnotations) - && (MessageAnnotations == that.MessageAnnotations) && (Properties == that.Properties) - && (ApplicationProperties == that.ApplicationProperties) && (Footer == that.Footer) - && (BodyType == that.BodyType) && (m_amqpValueBody == that.m_amqpValueBody) - && (m_amqpSequenceBody == that.m_amqpSequenceBody) - && (m_binaryDataBody == that.m_binaryDataBody); + auto serializedMessageProperties = MessageProperties::Serialize(message.Properties); + rv.insert(rv.end(), serializedMessageProperties.begin(), serializedMessageProperties.end()); } - size_t AmqpMessage::GetSerializedSize(AmqpMessage const& message) + if (!message.ApplicationProperties.empty()) { - size_t serializedSize{}; - - serializedSize += MessageHeader::GetSerializedSize(message.Header); - serializedSize += AmqpValue::GetSerializedSize(message.MessageAnnotations); - serializedSize += MessageProperties::GetSerializedSize(message.Properties); - - // ApplicationProperties is a map of string to value, we need to convert it to an AmqpMap. + AmqpMap appProperties; + for (auto const& val : message.ApplicationProperties) { - AmqpMap appProperties; - for (auto const& val : message.ApplicationProperties) + if ((val.second.GetType() == AmqpValueType::List) + || (val.second.GetType() == AmqpValueType::Map) + || (val.second.GetType() == AmqpValueType::Composite) + || (val.second.GetType() == AmqpValueType::Described)) { - if ((val.second.GetType() == AmqpValueType::List) - || (val.second.GetType() == AmqpValueType::Map) - || (val.second.GetType() == AmqpValueType::Composite) - || (val.second.GetType() == AmqpValueType::Described)) - { - throw std::runtime_error( - "Message Application Property values must be simple value types"); - } - appProperties.emplace(val); + throw std::runtime_error( + "Message Application Property values must be simple value types"); } - serializedSize += AmqpValue::GetSerializedSize(appProperties); + appProperties.emplace(val); } + AmqpValue propertiesValue{amqpvalue_create_application_properties(AmqpValue{appProperties})}; + auto serializedApplicationProperties = AmqpValue::Serialize(propertiesValue); + rv.insert( + rv.end(), serializedApplicationProperties.begin(), serializedApplicationProperties.end()); + } - switch (message.BodyType) - { - default: - case MessageBodyType::Invalid: - throw std::runtime_error("Invalid message body type."); - - case MessageBodyType::Value: - serializedSize += AmqpValue::GetSerializedSize(message.m_amqpValueBody); - break; - case MessageBodyType::Data: - for (auto const& val : message.m_binaryDataBody) - { - serializedSize += AmqpValue::GetSerializedSize(val); - } - break; - case MessageBodyType::Sequence: - for (auto const& val : message.m_amqpSequenceBody) - { - serializedSize += AmqpValue::GetSerializedSize(val); - } - break; + switch (message.BodyType) + { + default: + case MessageBodyType::Invalid: + throw std::runtime_error("Invalid message body type."); + + case MessageBodyType::Value: { + // The message body element is an AMQP Described type, create one and serialize the + // described body. + AmqpDescribed describedBody( + static_cast(AmqpDescriptors::DataAmqpValue), message.m_amqpValueBody); + auto serializedBodyValue = AmqpValue::Serialize(describedBody); + rv.insert(rv.end(), serializedBodyValue.begin(), serializedBodyValue.end()); + } + break; + case MessageBodyType::Data: + for (auto const& val : message.m_binaryDataBody) + { + AmqpDescribed describedBody(static_cast(AmqpDescriptors::DataBinary), val); + auto serializedBodyValue = AmqpValue::Serialize(describedBody); + rv.insert(rv.end(), serializedBodyValue.begin(), serializedBodyValue.end()); + } + break; + case MessageBodyType::Sequence: { + for (auto const& val : message.m_amqpSequenceBody) + { + AmqpDescribed describedBody( + static_cast(AmqpDescriptors::DataAmqpSequence), val); + auto serializedBodyValue = AmqpValue::Serialize(describedBody); + rv.insert(rv.end(), serializedBodyValue.begin(), serializedBodyValue.end()); + } } - return serializedSize; } - - std::vector AmqpMessage::Serialize(AmqpMessage const& message) + if (!message.Footer.empty()) { - // size_t serializedSize = AmqpMessage::GetSerializedSize(message); + AmqpValue footer{amqpvalue_create_footer(AmqpValue{message.Footer})}; + auto serializedFooter = AmqpValue::Serialize(footer); + rv.insert(rv.end(), serializedFooter.begin(), serializedFooter.end()); + } - std::vector rv; + return rv; + } - // Append the message Header to the serialized message. - if (message.Header.ShouldSerialize()) - { - auto serializedHeader = MessageHeader::Serialize(message.Header); - rv.insert(rv.end(), serializedHeader.begin(), serializedHeader.end()); - } - if (!message.DeliveryAnnotations.empty()) - { - AmqpValue deliveryAnnotations{ - amqpvalue_create_delivery_annotations(AmqpValue{message.DeliveryAnnotations})}; - auto serializedDeliveryAnnotations = AmqpValue::Serialize(deliveryAnnotations); - rv.insert( - rv.end(), serializedDeliveryAnnotations.begin(), serializedDeliveryAnnotations.end()); - } - if (!message.MessageAnnotations.empty()) - { - AmqpValue messageAnnotations{ - amqpvalue_create_message_annotations(AmqpValue{message.MessageAnnotations})}; - auto serializedAnnotations = AmqpValue::Serialize(messageAnnotations); - rv.insert(rv.end(), serializedAnnotations.begin(), serializedAnnotations.end()); - } + // The message fields, in their expected order. + std::vector expectedMessageFields{ + AmqpDescriptors::Header, + AmqpDescriptors::DeliveryAnnotations, + AmqpDescriptors::MessageAnnotations, + AmqpDescriptors::Properties, + AmqpDescriptors::ApplicationProperties}; - if (message.Properties.ShouldSerialize()) + namespace { + class AmqpMessageDeserializer { + public: + AmqpMessageDeserializer() + : m_decoder{amqpvalue_decoder_create(OnAmqpMessageFieldDecodedFn, this)} { - auto serializedMessageProperties = MessageProperties::Serialize(message.Properties); - rv.insert(rv.end(), serializedMessageProperties.begin(), serializedMessageProperties.end()); } - if (!message.ApplicationProperties.empty()) + AmqpMessage operator()(std::uint8_t const* data, size_t size) const { - AmqpMap appProperties; - for (auto const& val : message.ApplicationProperties) + if (amqpvalue_decode_bytes(m_decoder.get(), data, size)) { - if ((val.second.GetType() == AmqpValueType::List) - || (val.second.GetType() == AmqpValueType::Map) - || (val.second.GetType() == AmqpValueType::Composite) - || (val.second.GetType() == AmqpValueType::Described)) - { - throw std::runtime_error( - "Message Application Property values must be simple value types"); - } - appProperties.emplace(val); + throw std::runtime_error("Could not decode object"); } - AmqpValue propertiesValue{ - amqpvalue_create_application_properties(AmqpValue{appProperties})}; - auto serializedApplicationProperties = AmqpValue::Serialize(propertiesValue); - rv.insert( - rv.end(), - serializedApplicationProperties.begin(), - serializedApplicationProperties.end()); + return m_decodedValue; } - switch (message.BodyType) - { - default: - case MessageBodyType::Invalid: - throw std::runtime_error("Invalid message body type."); + private: + UniqueAmqpDecoderHandle m_decoder; + AmqpMessage m_decodedValue; - case MessageBodyType::Value: { - // The message body element is an AMQP Described type, create one and serialize the - // described body. - AmqpDescribed describedBody( - static_cast(AmqpDescriptors::DataAmqpValue), message.m_amqpValueBody); - auto serializedBodyValue = AmqpValue::Serialize(describedBody); - rv.insert(rv.end(), serializedBodyValue.begin(), serializedBodyValue.end()); - } - break; - case MessageBodyType::Data: - for (auto const& val : message.m_binaryDataBody) - { - AmqpDescribed describedBody( - static_cast(AmqpDescriptors::DataBinary), val); - auto serializedBodyValue = AmqpValue::Serialize(describedBody); - rv.insert(rv.end(), serializedBodyValue.begin(), serializedBodyValue.end()); - } - break; - case MessageBodyType::Sequence: { - for (auto const& val : message.m_amqpSequenceBody) - { - AmqpDescribed describedBody( - static_cast(AmqpDescriptors::DataAmqpSequence), val); - auto serializedBodyValue = AmqpValue::Serialize(describedBody); - rv.insert(rv.end(), serializedBodyValue.begin(), serializedBodyValue.end()); - } - } - } - if (!message.Footer.empty()) + // Invoked on each descriptor encountered while decrypting the message. + static void OnAmqpMessageFieldDecodedFn(void* context, AMQP_VALUE value) { - AmqpValue footer{amqpvalue_create_footer(AmqpValue{message.Footer})}; - auto serializedFooter = AmqpValue::Serialize(footer); - rv.insert(rv.end(), serializedFooter.begin(), serializedFooter.end()); - } - - return rv; - } - - // The message fields, in their expected order. - std::vector expectedMessageFields{ - AmqpDescriptors::Header, - AmqpDescriptors::DeliveryAnnotations, - AmqpDescriptors::MessageAnnotations, - AmqpDescriptors::Properties, - AmqpDescriptors::ApplicationProperties}; + auto deserializer = static_cast(context); - namespace { - class AmqpMessageDeserializer { - public: - AmqpMessageDeserializer() - : m_decoder{amqpvalue_decoder_create(OnAmqpMessageFieldDecodedFn, this)} - { - } + deserializer->OnAmqpMessageFieldDecoded(value); + } - AmqpMessage operator()(std::uint8_t const* data, size_t size) const + void OnAmqpMessageFieldDecoded(AmqpValue value) + { + if (value.GetType() != AmqpValueType::Described) { - if (amqpvalue_decode_bytes(m_decoder.get(), data, size)) - { - throw std::runtime_error("Could not decode object"); - } - return m_decodedValue; + throw std::runtime_error("Decoded message field whose type is NOT described."); } - - private: - UniqueAmqpDecoderHandle m_decoder; - AmqpMessage m_decodedValue; - - // Invoked on each descriptor encountered while decrypting the message. - static void OnAmqpMessageFieldDecodedFn(void* context, AMQP_VALUE value) + AmqpDescribed describedType{value.AsDescribed()}; + if (describedType.GetDescriptor().GetType() != AmqpValueType::Ulong) { - auto deserializer = static_cast(context); - - deserializer->OnAmqpMessageFieldDecoded(value); + throw std::runtime_error("Decoded message field MUST be a LONG type."); } - - void OnAmqpMessageFieldDecoded(AmqpValue value) + switch (static_cast(static_cast(describedType.GetDescriptor()))) { - if (value.GetType() != AmqpValueType::Described) - { - throw std::runtime_error("Decoded message field whose type is NOT described."); - } - AmqpDescribed describedType{value.AsDescribed()}; - if (describedType.GetDescriptor().GetType() != AmqpValueType::Ulong) - { - throw std::runtime_error("Decoded message field MUST be a LONG type."); + case AmqpDescriptors::Header: { + UniqueMessageHeaderHandle messageHeader; + HEADER_HANDLE h; + if (amqpvalue_get_header(value, &h)) + { + throw std::runtime_error("Could not convert field to header."); + } + messageHeader.reset(h); + h = nullptr; + m_decodedValue.Header = _internal::MessageHeaderFactory::FromUamqp(messageHeader); + break; } - switch ( - static_cast(static_cast(describedType.GetDescriptor()))) - { - case AmqpDescriptors::Header: { - UniqueMessageHeaderHandle messageHeader; - HEADER_HANDLE h; - if (amqpvalue_get_header(value, &h)) - { - throw std::runtime_error("Could not convert field to header."); - } - messageHeader.reset(h); - h = nullptr; - m_decodedValue.Header = _internal::MessageHeaderFactory::FromUamqp(messageHeader); - break; + case AmqpDescriptors::DeliveryAnnotations: + m_decodedValue.DeliveryAnnotations = describedType.GetValue().AsMap(); + break; + case AmqpDescriptors::MessageAnnotations: + m_decodedValue.MessageAnnotations = describedType.GetValue().AsMap(); + break; + case AmqpDescriptors::Properties: { + UniquePropertiesHandle properties; + PROPERTIES_HANDLE h; + if (amqpvalue_get_properties(value, &h)) + { + throw std::runtime_error("Could not convert field to header."); } - case AmqpDescriptors::DeliveryAnnotations: - m_decodedValue.DeliveryAnnotations = describedType.GetValue().AsMap(); - break; - case AmqpDescriptors::MessageAnnotations: - m_decodedValue.MessageAnnotations = describedType.GetValue().AsMap(); - break; - case AmqpDescriptors::Properties: { - UniquePropertiesHandle properties; - PROPERTIES_HANDLE h; - if (amqpvalue_get_properties(value, &h)) + properties.reset(h); + h = nullptr; + m_decodedValue.Properties = _internal::MessagePropertiesFactory::FromUamqp(properties); + break; + } + case AmqpDescriptors::ApplicationProperties: { + auto propertyMap = describedType.GetValue().AsMap(); + for (auto const& val : propertyMap) + { + if (val.first.GetType() != AmqpValueType::String) { - throw std::runtime_error("Could not convert field to header."); + throw std::runtime_error("Key of applications properties must be a string."); } - properties.reset(h); - h = nullptr; - m_decodedValue.Properties - = _internal::MessagePropertiesFactory::FromUamqp(properties); - break; - } - case AmqpDescriptors::ApplicationProperties: { - auto propertyMap = describedType.GetValue().AsMap(); - for (auto const& val : propertyMap) + if ((val.second.GetType() == AmqpValueType::List) + || (val.second.GetType() == AmqpValueType::Map) + || (val.second.GetType() == AmqpValueType::Composite) + || (val.second.GetType() == AmqpValueType::Described)) { - if (val.first.GetType() != AmqpValueType::String) - { - throw std::runtime_error("Key of applications properties must be a string."); - } - if ((val.second.GetType() == AmqpValueType::List) - || (val.second.GetType() == AmqpValueType::Map) - || (val.second.GetType() == AmqpValueType::Composite) - || (val.second.GetType() == AmqpValueType::Described)) - { - throw std::runtime_error( - "Message Application Property values must be simple value types"); - } - m_decodedValue.ApplicationProperties.emplace( - static_cast(val.first), val.second); + throw std::runtime_error( + "Message Application Property values must be simple value types"); } - break; + m_decodedValue.ApplicationProperties.emplace( + static_cast(val.first), val.second); } - case AmqpDescriptors::DataAmqpValue: - m_decodedValue.SetBody(describedType.GetValue()); - break; - case AmqpDescriptors::DataAmqpSequence: - m_decodedValue.SetBody(describedType.GetValue().AsList()); - break; - case AmqpDescriptors::DataBinary: - // Each call to SetBody will append the binary value to the vector of binary bodies. - m_decodedValue.SetBody(describedType.GetValue().AsBinary()); - break; - case AmqpDescriptors::Footer: - m_decodedValue.Footer = describedType.GetValue().AsMap(); - break; - default: - throw std::runtime_error("Unknown message descriptor."); + break; } + case AmqpDescriptors::DataAmqpValue: + m_decodedValue.SetBody(describedType.GetValue()); + break; + case AmqpDescriptors::DataAmqpSequence: + m_decodedValue.SetBody(describedType.GetValue().AsList()); + break; + case AmqpDescriptors::DataBinary: + // Each call to SetBody will append the binary value to the vector of binary bodies. + m_decodedValue.SetBody(describedType.GetValue().AsBinary()); + break; + case AmqpDescriptors::Footer: + m_decodedValue.Footer = describedType.GetValue().AsMap(); + break; + default: + throw std::runtime_error("Unknown message descriptor."); } - }; - } // namespace + } + }; + } // namespace - AmqpMessage AmqpMessage::Deserialize(std::uint8_t const* buffer, size_t size) - { - return AmqpMessageDeserializer{}(buffer, size); - } + AmqpMessage AmqpMessage::Deserialize(std::uint8_t const* buffer, size_t size) + { + return AmqpMessageDeserializer{}(buffer, size); + } - std::ostream& operator<<(std::ostream& os, AmqpMessage const& message) + std::ostream& operator<<(std::ostream& os, AmqpMessage const& message) + { + os << "Message: " << std::endl; + os << "Header " << message.Header << std::endl; + os << "Properties: " << message.Properties; + os << "Body: [" << std::endl; + switch (message.BodyType) { - os << "Message: " << std::endl; - os << "Header " << message.Header << std::endl; - os << "Properties: " << message.Properties; - os << "Body: [" << std::endl; - switch (message.BodyType) - { - case MessageBodyType::Invalid: // LCOV_EXCL_LINE - os << "Invalid"; // LCOV_EXCL_LINE - break; // LCOV_EXCL_LINE - case MessageBodyType::None: - os << "None"; - break; - case MessageBodyType::Data: { - os << "AMQP Data: ["; - auto bodyBinary = message.GetBodyAsBinary(); - uint8_t i = 0; - for (auto const& val : bodyBinary) - { - os << "Data: " << val << std::endl; - if (i < bodyBinary.size() - 1) - { - os << ", "; - } - i += 1; - } - os << "]"; - } + case MessageBodyType::Invalid: // LCOV_EXCL_LINE + os << "Invalid"; // LCOV_EXCL_LINE + break; // LCOV_EXCL_LINE + case MessageBodyType::None: + os << "None"; break; - case MessageBodyType::Sequence: { - os << "AMQP Sequence: ["; - auto bodySequence = message.GetBodyAsAmqpList(); - uint8_t i = 0; - for (auto const& val : bodySequence) + case MessageBodyType::Data: { + os << "AMQP Data: ["; + auto bodyBinary = message.GetBodyAsBinary(); + uint8_t i = 0; + for (auto const& val : bodyBinary) + { + os << "Data: " << val << std::endl; + if (i < bodyBinary.size() - 1) { - os << "Sequence: " << val << std::endl; - if (i < bodySequence.size() - 1) - { - os << ", "; - } - i += 1; + os << ", "; } - os << "]"; + i += 1; } - break; - case MessageBodyType::Value: - os << "AmqpValue: " << message.GetBodyAsAmqpValue(); - break; - } - os << "]"; - - { - if (!message.ApplicationProperties.empty()) + os << "]"; + } + break; + case MessageBodyType::Sequence: { + os << "AMQP Sequence: ["; + auto bodySequence = message.GetBodyAsAmqpList(); + uint8_t i = 0; + for (auto const& val : bodySequence) { - os << std::endl << "Application Properties: "; - for (auto const& val : message.ApplicationProperties) + os << "Sequence: " << val << std::endl; + if (i < bodySequence.size() - 1) { - os << "{" << val.first << ", " << val.second << "}"; + os << ", "; } + i += 1; } + os << "]"; } - if (!message.DeliveryAnnotations.empty()) + break; + case MessageBodyType::Value: + os << "AmqpValue: " << message.GetBodyAsAmqpValue(); + break; + } + os << "]"; + + { + if (!message.ApplicationProperties.empty()) { - os << std::endl << "Delivery Annotations: "; - for (auto const& val : message.DeliveryAnnotations) + os << std::endl << "Application Properties: "; + for (auto const& val : message.ApplicationProperties) { os << "{" << val.first << ", " << val.second << "}"; } } - if (!message.MessageAnnotations.empty()) + } + if (!message.DeliveryAnnotations.empty()) + { + os << std::endl << "Delivery Annotations: "; + for (auto const& val : message.DeliveryAnnotations) { - os << std::endl << "Message Annotations: "; - for (auto const& val : message.MessageAnnotations) - { - os << "{" << val.first << ", " << val.second << "}"; - } + os << "{" << val.first << ", " << val.second << "}"; + } + } + if (!message.MessageAnnotations.empty()) + { + os << std::endl << "Message Annotations: "; + for (auto const& val : message.MessageAnnotations) + { + os << "{" << val.first << ", " << val.second << "}"; } - if (!message.Footer.empty()) + } + if (!message.Footer.empty()) + { + os << "Footer: "; + for (auto const& val : message.Footer) { - os << "Footer: "; - for (auto const& val : message.Footer) - { - os << "{" << val.first << ", " << val.second << "}"; - } + os << "{" << val.first << ", " << val.second << "}"; } - return os; } + return os; + } }}}} // namespace Azure::Core::Amqp::Models diff --git a/sdk/core/azure-core-amqp/test/ut/amqp_value_tests.cpp b/sdk/core/azure-core-amqp/test/ut/amqp_value_tests.cpp index 28952d8821..2fc4e014d1 100644 --- a/sdk/core/azure-core-amqp/test/ut/amqp_value_tests.cpp +++ b/sdk/core/azure-core-amqp/test/ut/amqp_value_tests.cpp @@ -4,9 +4,9 @@ #include #include "azure/core/amqp/models/amqp_value.hpp" +#include #include #include -#include using namespace Azure::Core::Amqp::Models; @@ -632,7 +632,7 @@ TEST_F(TestValueSerialization, SerializeUlong) TEST_F(TestValueSerialization, SerializeByte) { { - std::vector testVector{0x51, 0x25}; + std::vector testVector{0x51, 0x25}; AmqpValue value{AmqpValue::Deserialize(testVector.data(), testVector.size())}; EXPECT_EQ(value.GetType(), AmqpValueType::Byte); EXPECT_EQ(0x25, static_cast(value)); @@ -644,7 +644,7 @@ TEST_F(TestValueSerialization, SerializeByte) } { - std::vector testVector{0x51, 0x89}; + std::vector testVector{0x51, 0x89}; AmqpValue value{AmqpValue::Deserialize(testVector.data(), testVector.size())}; EXPECT_EQ(value.GetType(), AmqpValueType::Byte); EXPECT_EQ(-119, static_cast(value)); diff --git a/sdk/core/azure-core-amqp/test/ut/mock_amqp_server.hpp b/sdk/core/azure-core-amqp/test/ut/mock_amqp_server.hpp index 666c55e2ff..8b3f6349f6 100644 --- a/sdk/core/azure-core-amqp/test/ut/mock_amqp_server.hpp +++ b/sdk/core/azure-core-amqp/test/ut/mock_amqp_server.hpp @@ -8,13 +8,13 @@ #include #include #include +#include #include #include #include #include #include #include -#include extern uint16_t FindAvailableSocket(); namespace MessageTests { From 4519870b550a9b7a63842aa83b741cc216914775 Mon Sep 17 00:00:00 2001 From: Larry Osterman Date: Thu, 27 Apr 2023 10:32:28 -0700 Subject: [PATCH 05/17] Fixed indentation issues pointed out by Ahson --- CMakeLists.txt | 5 +++-- sdk/core/CMakeLists.txt | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 9512554a82..4f6b280a4d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -113,9 +113,10 @@ add_subdirectory(sdk/storage) add_subdirectory(sdk/template) if(ENABLE_AZURE_CORE_AMQP) -#messaging sdks need AMQP -add_subdirectory(sdk/eventhubs) +# Messaging sdks need AMQP + add_subdirectory(sdk/eventhubs) endif( ) + if(BUILD_SAMPLES) add_subdirectory(samples/integration/vcpkg-all-smoke) endif() diff --git a/sdk/core/CMakeLists.txt b/sdk/core/CMakeLists.txt index 91c3a60ca2..2aed5613a1 100644 --- a/sdk/core/CMakeLists.txt +++ b/sdk/core/CMakeLists.txt @@ -15,7 +15,7 @@ endif() # AMQP is an optional feature, don't include it unless explicitly enabled. if (ENABLE_AZURE_CORE_AMQP) -add_subdirectory(azure-core-amqp) + add_subdirectory(azure-core-amqp) endif() if (BUILD_PERFORMANCE_TESTS) From 4e32f78e775e7f078be6886b0ed4f7084fe0fcab Mon Sep 17 00:00:00 2001 From: Larry Osterman Date: Thu, 27 Apr 2023 11:23:19 -0700 Subject: [PATCH 06/17] clang fix 3 --- .../azure-core-amqp/src/amqp/management.cpp | 388 +++++++++--------- 1 file changed, 198 insertions(+), 190 deletions(-) diff --git a/sdk/core/azure-core-amqp/src/amqp/management.cpp b/sdk/core/azure-core-amqp/src/amqp/management.cpp index 9fe9d46ccd..5559e181cd 100644 --- a/sdk/core/azure-core-amqp/src/amqp/management.cpp +++ b/sdk/core/azure-core-amqp/src/amqp/management.cpp @@ -23,216 +23,224 @@ void Azure::Core::_internal::UniqueHandleHelper::F } namespace Azure { namespace Core { namespace Amqp { - namespace _internal { - - /** - * @brief Create a new Management object instance. - * - * @param session - the session on which to create the instance. - * @param managementNodeName - the name of the message source and target. - * @param options - additional options for the Management object. - * @param managementEvents - events associated with the management object. - */ - Management::Management( - Session const& session, - std::string const& managementNodeName, - ManagementOptions const& options, - ManagementEvents* managementEvents) - : m_impl{std::make_shared<_detail::ManagementImpl>( - session.GetImpl(), - managementNodeName, - options, - managementEvents)} - { - } - - Management::operator bool() const { return static_cast(m_impl); }; - - /** - * @brief Open the management instance. - * - * @returns A tuple consisting of the status code for the open and the description of the - * status. - */ - ManagementOpenResult Management::Open(Azure::Core::Context const& context) - { - return m_impl->Open(context); - }; - - /** - * @brief Close the management instance. - */ - void Management::Close() { m_impl->Close(); }; - - std:: - tuple<_internal::ManagementOperationResult, std::uint32_t, std::string, Models::AmqpMessage> - Management::ExecuteOperation( - std::string const& operationToPerform, - std::string const& typeOfOperation, - std::string const& locales, - Azure::Core::Amqp::Models::AmqpMessage const& messageToSend, - Azure::Core::Context context) - { - return m_impl->ExecuteOperation( - operationToPerform, typeOfOperation, locales, messageToSend, context); - } - } // namespace _internal - namespace _detail { - ManagementImpl::ManagementImpl( - std::shared_ptr session, - std::string const& managementNodeName, - Azure::Core::Amqp::_internal::ManagementOptions const& options, - Azure::Core::Amqp::_internal::ManagementEvents* managementEvents) - : m_management{amqp_management_create(*session, managementNodeName.c_str())}, - m_options{options}, m_session{session}, m_eventHandler{managementEvents} - { - if (options.EnableTrace) + namespace _internal { + + /** + * @brief Create a new Management object instance. + * + * @param session - the session on which to create the instance. + * @param managementNodeName - the name of the message source and target. + * @param options - additional options for the Management object. + * @param managementEvents - events associated with the management object. + */ + Management::Management( + Session const& session, + std::string const& managementNodeName, + ManagementOptions const& options, + ManagementEvents* managementEvents) + : m_impl{std::make_shared<_detail::ManagementImpl>( + session.GetImpl(), + managementNodeName, + options, + managementEvents)} { - amqp_management_set_trace(m_management.get(), options.EnableTrace); } - if (!options.ExpectedStatusCodeKeyName.empty()) - { - amqp_management_set_override_status_code_key_name( - m_management.get(), options.ExpectedStatusCodeKeyName.c_str()); - } - if (!options.ExpectedStatusDescriptionKeyName.empty()) + + Management::operator bool() const { return static_cast(m_impl); } + + /** + * @brief Open the management instance. + * + * @returns A tuple consisting of the status code for the open and the description of the + * status. + */ + ManagementOpenResult Management::Open(Azure::Core::Context const& context) { - amqp_management_set_override_status_description_key_name( - m_management.get(), options.ExpectedStatusDescriptionKeyName.c_str()); + return m_impl->Open(context); } - } - ManagementImpl::~ManagementImpl() noexcept { m_eventHandler = nullptr; } - - ManagementImpl::operator bool() const { return static_cast(m_management); }; - - _internal::ManagementOpenResult ManagementImpl::Open(Azure::Core::Context const& context) - { - if (amqp_management_open_async( - m_management.get(), - ManagementImpl::OnOpenCompleteFn, - this, - ManagementImpl::OnManagementErrorFn, - this)) + + /** + * @brief Close the management instance. + */ + void Management::Close() { m_impl->Close(); } + + std::tuple< + _internal::ManagementOperationResult, + std::uint32_t, + std::string, + Models::AmqpMessage> + Management::ExecuteOperation( + std::string const& operationToPerform, + std::string const& typeOfOperation, + std::string const& locales, + Azure::Core::Amqp::Models::AmqpMessage const& messageToSend, + Azure::Core::Context context) { - throw std::runtime_error("Could not open management object."); + return m_impl->ExecuteOperation( + operationToPerform, typeOfOperation, locales, messageToSend, context); } - auto result - = m_openCompleteQueue.WaitForPolledResult(context, *m_session->GetConnectionToPoll()); - if (result) + } // namespace _internal + namespace _detail { + ManagementImpl::ManagementImpl( + std::shared_ptr session, + std::string const& managementNodeName, + Azure::Core::Amqp::_internal::ManagementOptions const& options, + Azure::Core::Amqp::_internal::ManagementEvents* managementEvents) + : m_management{amqp_management_create(*session, managementNodeName.c_str())}, + m_options{options}, m_session{session}, m_eventHandler{managementEvents} { - switch (std::get<0>(*result)) + if (options.EnableTrace) { - case AMQP_MANAGEMENT_OPEN_OK: - return _internal::ManagementOpenResult::Ok; - case AMQP_MANAGEMENT_OPEN_ERROR: - return _internal::ManagementOpenResult::Error; - case AMQP_MANAGEMENT_OPEN_CANCELLED: - return _internal::ManagementOpenResult::Cancelled; - default: - throw std::runtime_error("Unknown management open result."); + amqp_management_set_trace(m_management.get(), options.EnableTrace); + } + if (!options.ExpectedStatusCodeKeyName.empty()) + { + amqp_management_set_override_status_code_key_name( + m_management.get(), options.ExpectedStatusCodeKeyName.c_str()); + } + if (!options.ExpectedStatusDescriptionKeyName.empty()) + { + amqp_management_set_override_status_description_key_name( + m_management.get(), options.ExpectedStatusDescriptionKeyName.c_str()); } } - throw Azure::Core::OperationCancelledException("Management Open operation was cancelled."); - } - - std:: - tuple<_internal::ManagementOperationResult, std::uint32_t, std::string, Models::AmqpMessage> - ManagementImpl::ExecuteOperation( - std::string const& operationToPerform, - std::string const& typeOfOperation, - std::string const& locales, - Azure::Core::Amqp::Models::AmqpMessage const& messageToSend, - Azure::Core::Context context) - { - if (!amqp_management_execute_operation_async( - m_management.get(), - operationToPerform.c_str(), - typeOfOperation.c_str(), - locales.c_str(), - Models::_internal::AmqpMessageFactory::ToUamqp(messageToSend).get(), - ManagementImpl::OnExecuteOperationCompleteFn, - this)) + ManagementImpl::~ManagementImpl() noexcept { m_eventHandler = nullptr; } + + ManagementImpl::operator bool() const { return static_cast(m_management); } + + _internal::ManagementOpenResult ManagementImpl::Open(Azure::Core::Context const& context) { - throw std::runtime_error("Could not execute operation."); + if (amqp_management_open_async( + m_management.get(), + ManagementImpl::OnOpenCompleteFn, + this, + ManagementImpl::OnManagementErrorFn, + this)) + { + throw std::runtime_error("Could not open management object."); + } + auto result + = m_openCompleteQueue.WaitForPolledResult(context, *m_session->GetConnectionToPoll()); + if (result) + { + switch (std::get<0>(*result)) + { + case AMQP_MANAGEMENT_OPEN_OK: + return _internal::ManagementOpenResult::Ok; + case AMQP_MANAGEMENT_OPEN_ERROR: + return _internal::ManagementOpenResult::Error; + case AMQP_MANAGEMENT_OPEN_CANCELLED: + return _internal::ManagementOpenResult::Cancelled; + default: + throw std::runtime_error("Unknown management open result."); + } + } + throw Azure::Core::OperationCancelledException("Management Open operation was cancelled."); } - auto result = m_messageQueue.WaitForPolledResult(context, *m_session->GetConnectionToPoll()); - if (result) + std::tuple< + _internal::ManagementOperationResult, + std::uint32_t, + std::string, + Models::AmqpMessage> + ManagementImpl::ExecuteOperation( + std::string const& operationToPerform, + std::string const& typeOfOperation, + std::string const& locales, + Azure::Core::Amqp::Models::AmqpMessage const& messageToSend, + Azure::Core::Context context) { - return std::move(*result); + if (!amqp_management_execute_operation_async( + m_management.get(), + operationToPerform.c_str(), + typeOfOperation.c_str(), + locales.c_str(), + Models::_internal::AmqpMessageFactory::ToUamqp(messageToSend).get(), + ManagementImpl::OnExecuteOperationCompleteFn, + this)) + { + throw std::runtime_error("Could not execute operation."); + } + + auto result + = m_messageQueue.WaitForPolledResult(context, *m_session->GetConnectionToPoll()); + if (result) + { + return std::move(*result); + } + else + { + throw Azure::Core::OperationCancelledException("Management operation cancelled."); + } } - else + + void ManagementImpl::Close() { amqp_management_close(m_management.get()); } + + void ManagementImpl::OnOpenCompleteFn(void* context, AMQP_MANAGEMENT_OPEN_RESULT openResult) { - throw Azure::Core::OperationCancelledException("Management operation cancelled."); + ManagementImpl* management = static_cast(context); + management->m_openCompleteQueue.CompleteOperation(openResult); } - } - - void ManagementImpl::Close() { amqp_management_close(m_management.get()); } - - void ManagementImpl::OnOpenCompleteFn(void* context, AMQP_MANAGEMENT_OPEN_RESULT openResult) - { - ManagementImpl* management = static_cast(context); - management->m_openCompleteQueue.CompleteOperation(openResult); - } - - void ManagementImpl::OnManagementErrorFn(void* context) - { - Azure::Core::Diagnostics::_internal::Log::Write( - Azure::Core::Diagnostics::Logger::Level::Error, "Error processing management operation."); - ManagementImpl* management = static_cast(context); - if (management->m_eventHandler) + + void ManagementImpl::OnManagementErrorFn(void* context) { - management->m_eventHandler->OnError(); + Azure::Core::Diagnostics::_internal::Log::Write( + Azure::Core::Diagnostics::Logger::Level::Error, + "Error processing management operation."); + ManagementImpl* management = static_cast(context); + if (management->m_eventHandler) + { + management->m_eventHandler->OnError(); + } + management->m_messageQueue.CompleteOperation( + _internal::ManagementOperationResult::Error, + 0, + "Error processing management operation.", + Models::AmqpMessage()); } - management->m_messageQueue.CompleteOperation( - _internal::ManagementOperationResult::Error, - 0, - "Error processing management operation.", - Models::AmqpMessage()); - }; - - void ManagementImpl::OnExecuteOperationCompleteFn( - void* context, - AMQP_MANAGEMENT_EXECUTE_OPERATION_RESULT result, - std::uint32_t statusCode, - const char* statusDescription, - MESSAGE_HANDLE message) - { - ManagementImpl* management = static_cast(context); - switch (result) + + void ManagementImpl::OnExecuteOperationCompleteFn( + void* context, + AMQP_MANAGEMENT_EXECUTE_OPERATION_RESULT result, + std::uint32_t statusCode, + const char* statusDescription, + MESSAGE_HANDLE message) { - case AMQP_MANAGEMENT_EXECUTE_OPERATION_OK: - management->m_messageQueue.CompleteOperation( - _internal::ManagementOperationResult::Ok, - statusCode, - (statusDescription ? statusDescription : std::string()), - Models::_internal::AmqpMessageFactory::FromUamqp(message)); - break; - case AMQP_MANAGEMENT_EXECUTE_OPERATION_ERROR: - management->m_messageQueue.CompleteOperation( - _internal::ManagementOperationResult::Error, - statusCode, - (statusDescription ? statusDescription : std::string()), - Models::_internal::AmqpMessageFactory::FromUamqp(message)); - break; - case AMQP_MANAGEMENT_EXECUTE_OPERATION_FAILED_BAD_STATUS: - management->m_messageQueue.CompleteOperation( - _internal::ManagementOperationResult::FailedBadStatus, - statusCode, - (statusDescription ? statusDescription : std::string()), - Models::_internal::AmqpMessageFactory::FromUamqp(message)); - break; - case AMQP_MANAGEMENT_EXECUTE_OPERATION_INSTANCE_CLOSED: - management->m_messageQueue.CompleteOperation( - _internal::ManagementOperationResult::InstanceClosed, - statusCode, - (statusDescription ? statusDescription : std::string()), - Models::_internal::AmqpMessageFactory::FromUamqp(message)); - break; - default: - throw std::runtime_error("Unknown management status."); + ManagementImpl* management = static_cast(context); + switch (result) + { + case AMQP_MANAGEMENT_EXECUTE_OPERATION_OK: + management->m_messageQueue.CompleteOperation( + _internal::ManagementOperationResult::Ok, + statusCode, + (statusDescription ? statusDescription : std::string()), + Models::_internal::AmqpMessageFactory::FromUamqp(message)); + break; + case AMQP_MANAGEMENT_EXECUTE_OPERATION_ERROR: + management->m_messageQueue.CompleteOperation( + _internal::ManagementOperationResult::Error, + statusCode, + (statusDescription ? statusDescription : std::string()), + Models::_internal::AmqpMessageFactory::FromUamqp(message)); + break; + case AMQP_MANAGEMENT_EXECUTE_OPERATION_FAILED_BAD_STATUS: + management->m_messageQueue.CompleteOperation( + _internal::ManagementOperationResult::FailedBadStatus, + statusCode, + (statusDescription ? statusDescription : std::string()), + Models::_internal::AmqpMessageFactory::FromUamqp(message)); + break; + case AMQP_MANAGEMENT_EXECUTE_OPERATION_INSTANCE_CLOSED: + management->m_messageQueue.CompleteOperation( + _internal::ManagementOperationResult::InstanceClosed, + statusCode, + (statusDescription ? statusDescription : std::string()), + Models::_internal::AmqpMessageFactory::FromUamqp(message)); + break; + default: + throw std::runtime_error("Unknown management status."); + } } - } - } // namespace _detail + } // namespace _detail }}} // namespace Azure::Core::Amqp From 170105a6f6efa76ae3ce4854142b96362d14faf5 Mon Sep 17 00:00:00 2001 From: Larry Osterman Date: Thu, 27 Apr 2023 11:24:57 -0700 Subject: [PATCH 07/17] clang-format --- .../azure-core-amqp/src/amqp/management.cpp | 388 +++++++++--------- 1 file changed, 190 insertions(+), 198 deletions(-) diff --git a/sdk/core/azure-core-amqp/src/amqp/management.cpp b/sdk/core/azure-core-amqp/src/amqp/management.cpp index 5559e181cd..a240432d64 100644 --- a/sdk/core/azure-core-amqp/src/amqp/management.cpp +++ b/sdk/core/azure-core-amqp/src/amqp/management.cpp @@ -23,224 +23,216 @@ void Azure::Core::_internal::UniqueHandleHelper::F } namespace Azure { namespace Core { namespace Amqp { - namespace _internal { - - /** - * @brief Create a new Management object instance. - * - * @param session - the session on which to create the instance. - * @param managementNodeName - the name of the message source and target. - * @param options - additional options for the Management object. - * @param managementEvents - events associated with the management object. - */ - Management::Management( - Session const& session, - std::string const& managementNodeName, - ManagementOptions const& options, - ManagementEvents* managementEvents) - : m_impl{std::make_shared<_detail::ManagementImpl>( - session.GetImpl(), - managementNodeName, - options, - managementEvents)} + namespace _internal { + + /** + * @brief Create a new Management object instance. + * + * @param session - the session on which to create the instance. + * @param managementNodeName - the name of the message source and target. + * @param options - additional options for the Management object. + * @param managementEvents - events associated with the management object. + */ + Management::Management( + Session const& session, + std::string const& managementNodeName, + ManagementOptions const& options, + ManagementEvents* managementEvents) + : m_impl{std::make_shared<_detail::ManagementImpl>( + session.GetImpl(), + managementNodeName, + options, + managementEvents)} + { + } + + Management::operator bool() const { return static_cast(m_impl); } + + /** + * @brief Open the management instance. + * + * @returns A tuple consisting of the status code for the open and the description of the + * status. + */ + ManagementOpenResult Management::Open(Azure::Core::Context const& context) + { + return m_impl->Open(context); + } + + /** + * @brief Close the management instance. + */ + void Management::Close() { m_impl->Close(); } + + std:: + tuple<_internal::ManagementOperationResult, std::uint32_t, std::string, Models::AmqpMessage> + Management::ExecuteOperation( + std::string const& operationToPerform, + std::string const& typeOfOperation, + std::string const& locales, + Azure::Core::Amqp::Models::AmqpMessage const& messageToSend, + Azure::Core::Context context) + { + return m_impl->ExecuteOperation( + operationToPerform, typeOfOperation, locales, messageToSend, context); + } + } // namespace _internal + namespace _detail { + ManagementImpl::ManagementImpl( + std::shared_ptr session, + std::string const& managementNodeName, + Azure::Core::Amqp::_internal::ManagementOptions const& options, + Azure::Core::Amqp::_internal::ManagementEvents* managementEvents) + : m_management{amqp_management_create(*session, managementNodeName.c_str())}, + m_options{options}, m_session{session}, m_eventHandler{managementEvents} + { + if (options.EnableTrace) { + amqp_management_set_trace(m_management.get(), options.EnableTrace); } - - Management::operator bool() const { return static_cast(m_impl); } - - /** - * @brief Open the management instance. - * - * @returns A tuple consisting of the status code for the open and the description of the - * status. - */ - ManagementOpenResult Management::Open(Azure::Core::Context const& context) + if (!options.ExpectedStatusCodeKeyName.empty()) { - return m_impl->Open(context); + amqp_management_set_override_status_code_key_name( + m_management.get(), options.ExpectedStatusCodeKeyName.c_str()); } - - /** - * @brief Close the management instance. - */ - void Management::Close() { m_impl->Close(); } - - std::tuple< - _internal::ManagementOperationResult, - std::uint32_t, - std::string, - Models::AmqpMessage> - Management::ExecuteOperation( - std::string const& operationToPerform, - std::string const& typeOfOperation, - std::string const& locales, - Azure::Core::Amqp::Models::AmqpMessage const& messageToSend, - Azure::Core::Context context) + if (!options.ExpectedStatusDescriptionKeyName.empty()) { - return m_impl->ExecuteOperation( - operationToPerform, typeOfOperation, locales, messageToSend, context); + amqp_management_set_override_status_description_key_name( + m_management.get(), options.ExpectedStatusDescriptionKeyName.c_str()); } - } // namespace _internal - namespace _detail { - ManagementImpl::ManagementImpl( - std::shared_ptr session, - std::string const& managementNodeName, - Azure::Core::Amqp::_internal::ManagementOptions const& options, - Azure::Core::Amqp::_internal::ManagementEvents* managementEvents) - : m_management{amqp_management_create(*session, managementNodeName.c_str())}, - m_options{options}, m_session{session}, m_eventHandler{managementEvents} + } + ManagementImpl::~ManagementImpl() noexcept { m_eventHandler = nullptr; } + + ManagementImpl::operator bool() const { return static_cast(m_management); } + + _internal::ManagementOpenResult ManagementImpl::Open(Azure::Core::Context const& context) + { + if (amqp_management_open_async( + m_management.get(), + ManagementImpl::OnOpenCompleteFn, + this, + ManagementImpl::OnManagementErrorFn, + this)) { - if (options.EnableTrace) - { - amqp_management_set_trace(m_management.get(), options.EnableTrace); - } - if (!options.ExpectedStatusCodeKeyName.empty()) - { - amqp_management_set_override_status_code_key_name( - m_management.get(), options.ExpectedStatusCodeKeyName.c_str()); - } - if (!options.ExpectedStatusDescriptionKeyName.empty()) - { - amqp_management_set_override_status_description_key_name( - m_management.get(), options.ExpectedStatusDescriptionKeyName.c_str()); - } + throw std::runtime_error("Could not open management object."); } - ManagementImpl::~ManagementImpl() noexcept { m_eventHandler = nullptr; } - - ManagementImpl::operator bool() const { return static_cast(m_management); } - - _internal::ManagementOpenResult ManagementImpl::Open(Azure::Core::Context const& context) + auto result + = m_openCompleteQueue.WaitForPolledResult(context, *m_session->GetConnectionToPoll()); + if (result) { - if (amqp_management_open_async( - m_management.get(), - ManagementImpl::OnOpenCompleteFn, - this, - ManagementImpl::OnManagementErrorFn, - this)) - { - throw std::runtime_error("Could not open management object."); - } - auto result - = m_openCompleteQueue.WaitForPolledResult(context, *m_session->GetConnectionToPoll()); - if (result) + switch (std::get<0>(*result)) { - switch (std::get<0>(*result)) - { - case AMQP_MANAGEMENT_OPEN_OK: - return _internal::ManagementOpenResult::Ok; - case AMQP_MANAGEMENT_OPEN_ERROR: - return _internal::ManagementOpenResult::Error; - case AMQP_MANAGEMENT_OPEN_CANCELLED: - return _internal::ManagementOpenResult::Cancelled; - default: - throw std::runtime_error("Unknown management open result."); - } + case AMQP_MANAGEMENT_OPEN_OK: + return _internal::ManagementOpenResult::Ok; + case AMQP_MANAGEMENT_OPEN_ERROR: + return _internal::ManagementOpenResult::Error; + case AMQP_MANAGEMENT_OPEN_CANCELLED: + return _internal::ManagementOpenResult::Cancelled; + default: + throw std::runtime_error("Unknown management open result."); } - throw Azure::Core::OperationCancelledException("Management Open operation was cancelled."); } - - std::tuple< - _internal::ManagementOperationResult, - std::uint32_t, - std::string, - Models::AmqpMessage> - ManagementImpl::ExecuteOperation( - std::string const& operationToPerform, - std::string const& typeOfOperation, - std::string const& locales, - Azure::Core::Amqp::Models::AmqpMessage const& messageToSend, - Azure::Core::Context context) + throw Azure::Core::OperationCancelledException("Management Open operation was cancelled."); + } + + std:: + tuple<_internal::ManagementOperationResult, std::uint32_t, std::string, Models::AmqpMessage> + ManagementImpl::ExecuteOperation( + std::string const& operationToPerform, + std::string const& typeOfOperation, + std::string const& locales, + Azure::Core::Amqp::Models::AmqpMessage const& messageToSend, + Azure::Core::Context context) + { + if (!amqp_management_execute_operation_async( + m_management.get(), + operationToPerform.c_str(), + typeOfOperation.c_str(), + locales.c_str(), + Models::_internal::AmqpMessageFactory::ToUamqp(messageToSend).get(), + ManagementImpl::OnExecuteOperationCompleteFn, + this)) { - if (!amqp_management_execute_operation_async( - m_management.get(), - operationToPerform.c_str(), - typeOfOperation.c_str(), - locales.c_str(), - Models::_internal::AmqpMessageFactory::ToUamqp(messageToSend).get(), - ManagementImpl::OnExecuteOperationCompleteFn, - this)) - { - throw std::runtime_error("Could not execute operation."); - } - - auto result - = m_messageQueue.WaitForPolledResult(context, *m_session->GetConnectionToPoll()); - if (result) - { - return std::move(*result); - } - else - { - throw Azure::Core::OperationCancelledException("Management operation cancelled."); - } + throw std::runtime_error("Could not execute operation."); } - void ManagementImpl::Close() { amqp_management_close(m_management.get()); } - - void ManagementImpl::OnOpenCompleteFn(void* context, AMQP_MANAGEMENT_OPEN_RESULT openResult) + auto result = m_messageQueue.WaitForPolledResult(context, *m_session->GetConnectionToPoll()); + if (result) { - ManagementImpl* management = static_cast(context); - management->m_openCompleteQueue.CompleteOperation(openResult); + return std::move(*result); } - - void ManagementImpl::OnManagementErrorFn(void* context) + else { - Azure::Core::Diagnostics::_internal::Log::Write( - Azure::Core::Diagnostics::Logger::Level::Error, - "Error processing management operation."); - ManagementImpl* management = static_cast(context); - if (management->m_eventHandler) - { - management->m_eventHandler->OnError(); - } - management->m_messageQueue.CompleteOperation( - _internal::ManagementOperationResult::Error, - 0, - "Error processing management operation.", - Models::AmqpMessage()); + throw Azure::Core::OperationCancelledException("Management operation cancelled."); } - - void ManagementImpl::OnExecuteOperationCompleteFn( - void* context, - AMQP_MANAGEMENT_EXECUTE_OPERATION_RESULT result, - std::uint32_t statusCode, - const char* statusDescription, - MESSAGE_HANDLE message) + } + + void ManagementImpl::Close() { amqp_management_close(m_management.get()); } + + void ManagementImpl::OnOpenCompleteFn(void* context, AMQP_MANAGEMENT_OPEN_RESULT openResult) + { + ManagementImpl* management = static_cast(context); + management->m_openCompleteQueue.CompleteOperation(openResult); + } + + void ManagementImpl::OnManagementErrorFn(void* context) + { + Azure::Core::Diagnostics::_internal::Log::Write( + Azure::Core::Diagnostics::Logger::Level::Error, "Error processing management operation."); + ManagementImpl* management = static_cast(context); + if (management->m_eventHandler) { - ManagementImpl* management = static_cast(context); - switch (result) - { - case AMQP_MANAGEMENT_EXECUTE_OPERATION_OK: - management->m_messageQueue.CompleteOperation( - _internal::ManagementOperationResult::Ok, - statusCode, - (statusDescription ? statusDescription : std::string()), - Models::_internal::AmqpMessageFactory::FromUamqp(message)); - break; - case AMQP_MANAGEMENT_EXECUTE_OPERATION_ERROR: - management->m_messageQueue.CompleteOperation( - _internal::ManagementOperationResult::Error, - statusCode, - (statusDescription ? statusDescription : std::string()), - Models::_internal::AmqpMessageFactory::FromUamqp(message)); - break; - case AMQP_MANAGEMENT_EXECUTE_OPERATION_FAILED_BAD_STATUS: - management->m_messageQueue.CompleteOperation( - _internal::ManagementOperationResult::FailedBadStatus, - statusCode, - (statusDescription ? statusDescription : std::string()), - Models::_internal::AmqpMessageFactory::FromUamqp(message)); - break; - case AMQP_MANAGEMENT_EXECUTE_OPERATION_INSTANCE_CLOSED: - management->m_messageQueue.CompleteOperation( - _internal::ManagementOperationResult::InstanceClosed, - statusCode, - (statusDescription ? statusDescription : std::string()), - Models::_internal::AmqpMessageFactory::FromUamqp(message)); - break; - default: - throw std::runtime_error("Unknown management status."); - } + management->m_eventHandler->OnError(); + } + management->m_messageQueue.CompleteOperation( + _internal::ManagementOperationResult::Error, + 0, + "Error processing management operation.", + Models::AmqpMessage()); + } + + void ManagementImpl::OnExecuteOperationCompleteFn( + void* context, + AMQP_MANAGEMENT_EXECUTE_OPERATION_RESULT result, + std::uint32_t statusCode, + const char* statusDescription, + MESSAGE_HANDLE message) + { + ManagementImpl* management = static_cast(context); + switch (result) + { + case AMQP_MANAGEMENT_EXECUTE_OPERATION_OK: + management->m_messageQueue.CompleteOperation( + _internal::ManagementOperationResult::Ok, + statusCode, + (statusDescription ? statusDescription : std::string()), + Models::_internal::AmqpMessageFactory::FromUamqp(message)); + break; + case AMQP_MANAGEMENT_EXECUTE_OPERATION_ERROR: + management->m_messageQueue.CompleteOperation( + _internal::ManagementOperationResult::Error, + statusCode, + (statusDescription ? statusDescription : std::string()), + Models::_internal::AmqpMessageFactory::FromUamqp(message)); + break; + case AMQP_MANAGEMENT_EXECUTE_OPERATION_FAILED_BAD_STATUS: + management->m_messageQueue.CompleteOperation( + _internal::ManagementOperationResult::FailedBadStatus, + statusCode, + (statusDescription ? statusDescription : std::string()), + Models::_internal::AmqpMessageFactory::FromUamqp(message)); + break; + case AMQP_MANAGEMENT_EXECUTE_OPERATION_INSTANCE_CLOSED: + management->m_messageQueue.CompleteOperation( + _internal::ManagementOperationResult::InstanceClosed, + statusCode, + (statusDescription ? statusDescription : std::string()), + Models::_internal::AmqpMessageFactory::FromUamqp(message)); + break; + default: + throw std::runtime_error("Unknown management status."); } + } - } // namespace _detail + } // namespace _detail }}} // namespace Azure::Core::Amqp From df82714d1560cd6215bccbb3a928a6854313e36e Mon Sep 17 00:00:00 2001 From: Larry Osterman Date: Thu, 27 Apr 2023 12:29:43 -0700 Subject: [PATCH 08/17] clang fix 3 --- sdk/core/azure-core-amqp/test/ut/amqp_value_tests.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sdk/core/azure-core-amqp/test/ut/amqp_value_tests.cpp b/sdk/core/azure-core-amqp/test/ut/amqp_value_tests.cpp index 2fc4e014d1..5335b27f45 100644 --- a/sdk/core/azure-core-amqp/test/ut/amqp_value_tests.cpp +++ b/sdk/core/azure-core-amqp/test/ut/amqp_value_tests.cpp @@ -1148,7 +1148,7 @@ TEST_F(TestValueSerialization, SerializeList) std::vector testVector{0xd0, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00}; AmqpValue value{AmqpValue::Deserialize(testVector.data(), testVector.size())}; EXPECT_EQ(value.GetType(), AmqpValueType::List); - AmqpList list{value.AsList()}; + AmqpList list(value.AsList()); EXPECT_EQ(list.size(), 0); std::vector val = AmqpValue::Serialize(value); @@ -1184,7 +1184,7 @@ TEST_F(TestValueSerialization, SerializeList) AmqpValue value{AmqpValue::Deserialize(testVector.data(), testVector.size())}; EXPECT_EQ(value.GetType(), AmqpValueType::List); - AmqpList list{value.AsList()}; + AmqpList list(value.AsList()); EXPECT_EQ(list.size(), values.size()); EXPECT_TRUE(std::equal(values.begin(), values.end(), list.begin(), list.end())); @@ -1220,7 +1220,7 @@ TEST_F(TestValueSerialization, SerializeList) AmqpValue value{AmqpValue::Deserialize(testVector.data(), testVector.size())}; EXPECT_EQ(value.GetType(), AmqpValueType::List); - AmqpList list{value.AsList()}; + AmqpList list(value.AsList()); EXPECT_EQ(list.size(), values.size()); EXPECT_TRUE(std::equal(values.begin(), values.end(), list.begin(), list.end())); @@ -1261,7 +1261,7 @@ TEST_F(TestValueSerialization, SerializeList) AmqpValue value{AmqpValue::Deserialize(testVector.data(), testVector.size())}; EXPECT_EQ(value.GetType(), AmqpValueType::List); - AmqpList list{value.AsList()}; + AmqpList list(value.AsList()); EXPECT_EQ(list.size(), values.size()); EXPECT_TRUE(std::equal(values.begin(), values.end(), list.begin(), list.end())); From 69b42f59a3f9080555a4382f404004a779e725a0 Mon Sep 17 00:00:00 2001 From: Larry Osterman Date: Thu, 27 Apr 2023 12:39:03 -0700 Subject: [PATCH 09/17] clang fix 4 --- sdk/core/azure-core-amqp/test/ut/amqp_value_tests.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/core/azure-core-amqp/test/ut/amqp_value_tests.cpp b/sdk/core/azure-core-amqp/test/ut/amqp_value_tests.cpp index 5335b27f45..f835f0600f 100644 --- a/sdk/core/azure-core-amqp/test/ut/amqp_value_tests.cpp +++ b/sdk/core/azure-core-amqp/test/ut/amqp_value_tests.cpp @@ -1118,7 +1118,7 @@ TEST_F(TestValueSerialization, SerializeList) { std::vector testVector{0x45}; AmqpValue value{AmqpValue::Deserialize(testVector.data(), testVector.size())}; - AmqpList list{value.AsList()}; + AmqpList list(value.AsList()); EXPECT_EQ(list.size(), 0); } @@ -1136,7 +1136,7 @@ TEST_F(TestValueSerialization, SerializeList) std::vector testVector{0xc0, 0x01, 0x00}; AmqpValue value{AmqpValue::Deserialize(testVector.data(), testVector.size())}; EXPECT_EQ(value.GetType(), AmqpValueType::List); - AmqpList list{value.AsList()}; + AmqpList list(value.AsList()); EXPECT_EQ(list.size(), 0); std::vector val = AmqpValue::Serialize(value); From 375119100fac05aaf6215b98a8646f6ddf5bf852 Mon Sep 17 00:00:00 2001 From: Larry Osterman Date: Thu, 27 Apr 2023 14:15:18 -0700 Subject: [PATCH 10/17] clang fix 5 --- .../src/models/amqp_message.cpp | 1240 +++++++++-------- .../test/ut/amqp_value_tests.cpp | 20 +- 2 files changed, 681 insertions(+), 579 deletions(-) diff --git a/sdk/core/azure-core-amqp/src/models/amqp_message.cpp b/sdk/core/azure-core-amqp/src/models/amqp_message.cpp index 6cb6653590..ac1c736318 100644 --- a/sdk/core/azure-core-amqp/src/models/amqp_message.cpp +++ b/sdk/core/azure-core-amqp/src/models/amqp_message.cpp @@ -11,6 +11,7 @@ #include #include #include +#include namespace Azure { namespace Core { namespace _internal { void UniqueHandleHelper::FreeAmqpMessage(MESSAGE_HANDLE value) @@ -23,732 +24,833 @@ using namespace Azure::Core::Amqp::_detail; namespace Azure { namespace Core { namespace Amqp { namespace Models { - namespace { + namespace { - UniqueMessageHeaderHandle GetHeaderFromMessage(MESSAGE_HANDLE message) - { - if (message != nullptr) + UniqueMessageHeaderHandle GetHeaderFromMessage(MESSAGE_HANDLE message) { - HEADER_HANDLE headerValue; - if (!message_get_header(message, &headerValue)) + if (message != nullptr) { - return Azure::Core::_internal::UniqueHandle(headerValue); + HEADER_HANDLE headerValue; + if (!message_get_header(message, &headerValue)) + { + return Azure::Core::_internal::UniqueHandle(headerValue); + } } + return nullptr; } - return nullptr; - } - UniquePropertiesHandle GetPropertiesFromMessage(MESSAGE_HANDLE const& message) - { - if (message != nullptr) + UniquePropertiesHandle GetPropertiesFromMessage(MESSAGE_HANDLE const& message) { - PROPERTIES_HANDLE propertiesValue; - if (!message_get_properties(message, &propertiesValue)) + if (message != nullptr) { - return Azure::Core::_internal::UniqueHandle(propertiesValue); + PROPERTIES_HANDLE propertiesValue; + if (!message_get_properties(message, &propertiesValue)) + { + return Azure::Core::_internal::UniqueHandle(propertiesValue); + } } + return nullptr; } - return nullptr; - } - } // namespace - - AmqpMessage _internal::AmqpMessageFactory::FromUamqp(UniqueMessageHandle const& message) - { - return FromUamqp(message.get()); - } + } // namespace - AmqpMessage _internal::AmqpMessageFactory::FromUamqp(MESSAGE_HANDLE message) - { - if (message == nullptr) + AmqpMessage _internal::AmqpMessageFactory::FromUamqp(UniqueMessageHandle const& message) { - return AmqpMessage(nullptr); + return FromUamqp(message.get()); } - AmqpMessage rv; - rv.Header = _internal::MessageHeaderFactory::FromUamqp(GetHeaderFromMessage(message)); - rv.Properties - = _internal::MessagePropertiesFactory::FromUamqp(GetPropertiesFromMessage(message)); + AmqpMessage _internal::AmqpMessageFactory::FromUamqp(MESSAGE_HANDLE message) { - delivery_annotations annotationsVal; - // message_get_delivery_annotations returns a clone of the message annotations. - if (!message_get_delivery_annotations(message, &annotationsVal) && annotationsVal != nullptr) + if (message == nullptr) { - UniqueAmqpValueHandle deliveryAnnotations(annotationsVal); - auto deliveryMap = AmqpValue{deliveryAnnotations.get()}.AsMap(); - rv.DeliveryAnnotations = deliveryMap; + return AmqpMessage(nullptr); } - } - { - // message_get_message_annotations returns a clone of the message annotations. - AMQP_VALUE annotationVal; - if (!message_get_message_annotations(message, &annotationVal) && annotationVal) + AmqpMessage rv; + rv.Header = _internal::MessageHeaderFactory::FromUamqp(GetHeaderFromMessage(message)); + rv.Properties + = _internal::MessagePropertiesFactory::FromUamqp(GetPropertiesFromMessage(message)); + { - UniqueAmqpValueHandle messageAnnotations(annotationVal); - if (messageAnnotations) + delivery_annotations annotationsVal; + // message_get_delivery_annotations returns a clone of the message annotations. + if (!message_get_delivery_annotations(message, &annotationsVal) + && annotationsVal != nullptr) { - auto messageMap = AmqpValue{messageAnnotations.get()}.AsMap(); - rv.MessageAnnotations = messageMap; + UniqueAmqpValueHandle deliveryAnnotations(annotationsVal); + auto deliveryMap = AmqpValue{deliveryAnnotations.get()}.AsMap(); + rv.DeliveryAnnotations = deliveryMap; } } - } - { - /* - * The ApplicationProperties field in an AMQP message for uAMQP expects that the map value - * is wrapped as a described value. A described value has a ULONG descriptor value and a - * value type. - * - * Making things even more interesting, the ApplicationProperties field in an uAMQP message - * is asymmetric. - * - * The MessageSender class will wrap ApplicationProperties in a described value, so when - * setting application properties, the described value must NOT be present, but when - * decoding an application properties, the GetApplicationProperties method has to be able to - * handle both when the described value is present or not. - */ - AMQP_VALUE properties; - if (!message_get_application_properties(message, &properties) && properties) - { - UniqueAmqpValueHandle describedProperties(properties); - properties = nullptr; - if (describedProperties) + { + // message_get_message_annotations returns a clone of the message annotations. + AMQP_VALUE annotationVal; + if (!message_get_message_annotations(message, &annotationVal) && annotationVal) { - AMQP_VALUE value; - if (amqpvalue_get_type(describedProperties.get()) == AMQP_TYPE_DESCRIBED) + UniqueAmqpValueHandle messageAnnotations(annotationVal); + if (messageAnnotations) { - auto describedType = amqpvalue_get_inplace_descriptor(describedProperties.get()); - uint64_t describedTypeValue; - if (amqpvalue_get_ulong(describedType, &describedTypeValue)) + auto messageMap = AmqpValue{messageAnnotations.get()}.AsMap(); + rv.MessageAnnotations = messageMap; + } + } + } + { + /* + * The ApplicationProperties field in an AMQP message for uAMQP expects that the map value + * is wrapped as a described value. A described value has a ULONG descriptor value and a + * value type. + * + * Making things even more interesting, the ApplicationProperties field in an uAMQP message + * is asymmetric. + * + * The MessageSender class will wrap ApplicationProperties in a described value, so when + * setting application properties, the described value must NOT be present, but when + * decoding an application properties, the GetApplicationProperties method has to be able to + * handle both when the described value is present or not. + */ + AMQP_VALUE properties; + if (!message_get_application_properties(message, &properties) && properties) + { + UniqueAmqpValueHandle describedProperties(properties); + properties = nullptr; + if (describedProperties) + { + AMQP_VALUE value; + if (amqpvalue_get_type(describedProperties.get()) == AMQP_TYPE_DESCRIBED) { - throw std::runtime_error("Could not retrieve application properties described type."); + auto describedType = amqpvalue_get_inplace_descriptor(describedProperties.get()); + uint64_t describedTypeValue; + if (amqpvalue_get_ulong(describedType, &describedTypeValue)) + { + throw std::runtime_error( + "Could not retrieve application properties described type."); + } + if (describedTypeValue + != static_cast( + Azure::Core::Amqp::_detail::AmqpDescriptors::ApplicationProperties)) + { + throw std::runtime_error( + "Application Properties are not the corect described type."); + } + + value = amqpvalue_get_inplace_described_value(describedProperties.get()); } - if (describedTypeValue - != static_cast( - Azure::Core::Amqp::_detail::AmqpDescriptors::ApplicationProperties)) + else { - throw std::runtime_error("Application Properties are not the corect described type."); + value = describedProperties.get(); } - - value = amqpvalue_get_inplace_described_value(describedProperties.get()); - } - else - { - value = describedProperties.get(); - } - if (amqpvalue_get_type(value) != AMQP_TYPE_MAP) - { - throw std::runtime_error("Application Properties must be a map?!"); - } - auto appProperties = AmqpMap(value); - for (auto const& val : appProperties) - { - if (val.first.GetType() != AmqpValueType::String) + if (amqpvalue_get_type(value) != AMQP_TYPE_MAP) { - throw std::runtime_error("Key of Application Properties must be a string."); + throw std::runtime_error("Application Properties must be a map?!"); + } + auto appProperties = AmqpMap(value); + for (auto const& val : appProperties) + { + if (val.first.GetType() != AmqpValueType::String) + { + throw std::runtime_error("Key of Application Properties must be a string."); + } + rv.ApplicationProperties.emplace( + std::make_pair(static_cast(val.first), val.second)); } - rv.ApplicationProperties.emplace( - std::make_pair(static_cast(val.first), val.second)); } } } - } - { - annotations footerVal; - if (!message_get_footer(message, &footerVal) && footerVal) { - UniqueAmqpValueHandle footerAnnotations(footerVal); - footerVal = nullptr; - auto footerMap = AmqpValue{footerAnnotations.get()}.AsMap(); - rv.Footer = footerMap; + annotations footerVal; + if (!message_get_footer(message, &footerVal) && footerVal) + { + UniqueAmqpValueHandle footerAnnotations(footerVal); + footerVal = nullptr; + auto footerMap = AmqpValue{footerAnnotations.get()}.AsMap(); + rv.Footer = footerMap; + } } - } - { - MESSAGE_BODY_TYPE bodyType; - - if (!message_get_body_type(message, &bodyType)) { - switch (bodyType) + MESSAGE_BODY_TYPE bodyType; + + if (!message_get_body_type(message, &bodyType)) { - case MESSAGE_BODY_TYPE_NONE: - rv.BodyType = MessageBodyType::None; - break; - case MESSAGE_BODY_TYPE_DATA: { - size_t dataCount; - if (!message_get_body_amqp_data_count(message, &dataCount)) - { - for (auto i = 0ul; i < dataCount; i += 1) + switch (bodyType) + { + case MESSAGE_BODY_TYPE_NONE: + rv.BodyType = MessageBodyType::None; + break; + case MESSAGE_BODY_TYPE_DATA: { + size_t dataCount; + if (!message_get_body_amqp_data_count(message, &dataCount)) { - BINARY_DATA binaryValue; - if (!message_get_body_amqp_data_in_place(message, i, &binaryValue)) + for (auto i = 0ul; i < dataCount; i += 1) { - rv.m_binaryDataBody.push_back(AmqpBinaryData(std::vector( - binaryValue.bytes, binaryValue.bytes + binaryValue.length))); + BINARY_DATA binaryValue; + if (!message_get_body_amqp_data_in_place(message, i, &binaryValue)) + { + rv.m_binaryDataBody.push_back(AmqpBinaryData(std::vector( + binaryValue.bytes, binaryValue.bytes + binaryValue.length))); + } } } + rv.BodyType = MessageBodyType::Data; } - rv.BodyType = MessageBodyType::Data; - } - break; - case MESSAGE_BODY_TYPE_SEQUENCE: { + break; + case MESSAGE_BODY_TYPE_SEQUENCE: { - size_t sequenceCount; - if (!message_get_body_amqp_sequence_count(message, &sequenceCount)) - { - for (auto i = 0ul; i < sequenceCount; i += 1) + size_t sequenceCount; + if (!message_get_body_amqp_sequence_count(message, &sequenceCount)) { - AMQP_VALUE sequence; - if (!message_get_body_amqp_sequence_in_place(message, i, &sequence)) + for (auto i = 0ul; i < sequenceCount; i += 1) { - rv.m_amqpSequenceBody.push_back(sequence); + AMQP_VALUE sequence; + if (!message_get_body_amqp_sequence_in_place(message, i, &sequence)) + { + rv.m_amqpSequenceBody.push_back(sequence); + } } } + rv.BodyType = MessageBodyType::Sequence; } - rv.BodyType = MessageBodyType::Sequence; - } - break; - case MESSAGE_BODY_TYPE_VALUE: { - AMQP_VALUE bodyValue; - if (!message_get_body_amqp_value_in_place(message, &bodyValue)) - { - rv.m_amqpValueBody = bodyValue; + break; + case MESSAGE_BODY_TYPE_VALUE: { + AMQP_VALUE bodyValue; + if (!message_get_body_amqp_value_in_place(message, &bodyValue)) + { + rv.m_amqpValueBody = bodyValue; + } + rv.BodyType = MessageBodyType::Value; } - rv.BodyType = MessageBodyType::Value; + break; + case MESSAGE_BODY_TYPE_INVALID: + default: + throw std::runtime_error("Unknown body type."); } - break; - case MESSAGE_BODY_TYPE_INVALID: - default: - throw std::runtime_error("Unknown body type."); } } + return rv; } - return rv; - } - UniqueMessageHandle _internal::AmqpMessageFactory::ToUamqp(AmqpMessage const& message) - { - UniqueMessageHandle rv(message_create()); - - // AMQP 1.0 specifies a message format of 0. - if (message_set_message_format(rv.get(), AmqpMessageFormatValue)) + UniqueMessageHandle _internal::AmqpMessageFactory::ToUamqp(AmqpMessage const& message) { - throw std::runtime_error("Could not set destination message format."); - } + UniqueMessageHandle rv(message_create()); - if (message_set_header( - rv.get(), _internal::MessageHeaderFactory::ToUamqp(message.Header).get())) - { - throw std::runtime_error("Could not set message header."); - } - if (message_set_properties( - rv.get(), _internal::MessagePropertiesFactory::ToUamqp(message.Properties).get())) - { - throw std::runtime_error("Could not set message properties."); - } + // AMQP 1.0 specifies a message format of 0. + if (message_set_message_format(rv.get(), AmqpMessageFormatValue)) + { + throw std::runtime_error("Could not set destination message format."); + } - if (!message.DeliveryAnnotations.empty()) - { - if (message_set_delivery_annotations( - rv.get(), static_cast(message.DeliveryAnnotations).get())) + if (message_set_header( + rv.get(), _internal::MessageHeaderFactory::ToUamqp(message.Header).get())) { - throw std::runtime_error("Could not set delivery annotations."); + throw std::runtime_error("Could not set message header."); } - } - if (!message.MessageAnnotations.empty()) - { - if (message_set_message_annotations( - rv.get(), static_cast(message.MessageAnnotations).get())) + if (message_set_properties( + rv.get(), _internal::MessagePropertiesFactory::ToUamqp(message.Properties).get())) { - throw std::runtime_error("Could not set message annotations."); + throw std::runtime_error("Could not set message properties."); } - } - if (!message.ApplicationProperties.empty()) - { - AmqpMap appProperties; - for (auto const& val : message.ApplicationProperties) + + if (!message.DeliveryAnnotations.empty()) { - if ((val.second.GetType() == AmqpValueType::List) - || (val.second.GetType() == AmqpValueType::Map) - || (val.second.GetType() == AmqpValueType::Composite) - || (val.second.GetType() == AmqpValueType::Described)) + if (message_set_delivery_annotations( + rv.get(), static_cast(message.DeliveryAnnotations).get())) { - throw std::runtime_error( - "Message Application Property values must be simple value types"); + throw std::runtime_error("Could not set delivery annotations."); } - appProperties.emplace(val); } - if (message_set_application_properties( - rv.get(), static_cast(appProperties).get())) + if (!message.MessageAnnotations.empty()) { - throw std::runtime_error("Could not set application properties."); + if (message_set_message_annotations( + rv.get(), static_cast(message.MessageAnnotations).get())) + { + throw std::runtime_error("Could not set message annotations."); + } } - } - if (!message.Footer.empty()) - { - if (message_set_footer(rv.get(), static_cast(message.Footer).get())) + if (!message.ApplicationProperties.empty()) { - throw std::runtime_error("Could not set message annotations."); - } - } - switch (message.BodyType) - { - case MessageBodyType::None: - break; - case MessageBodyType::Data: - for (auto const& binaryVal : message.m_binaryDataBody) + AmqpMap appProperties; + for (auto const& val : message.ApplicationProperties) { - BINARY_DATA valueData; - valueData.bytes = binaryVal.data(); - valueData.length = static_cast(binaryVal.size()); - if (message_add_body_amqp_data(rv.get(), valueData)) + if ((val.second.GetType() == AmqpValueType::List) + || (val.second.GetType() == AmqpValueType::Map) + || (val.second.GetType() == AmqpValueType::Composite) + || (val.second.GetType() == AmqpValueType::Described)) { - throw std::runtime_error("Could not set message body AMQP sequence value."); + throw std::runtime_error( + "Message Application Property values must be simple value types"); } + appProperties.emplace(val); } - break; - case MessageBodyType::Sequence: - for (auto const& sequenceVal : message.m_amqpSequenceBody) + if (message_set_application_properties( + rv.get(), static_cast(appProperties).get())) { - if (message_add_body_amqp_sequence(rv.get(), AmqpValue(sequenceVal))) - { - throw std::runtime_error("Could not set message body AMQP sequence value."); - } + throw std::runtime_error("Could not set application properties."); } - break; - case MessageBodyType::Value: - if (message_set_body_amqp_value(rv.get(), message.m_amqpValueBody)) + } + if (!message.Footer.empty()) + { + if (message_set_footer(rv.get(), static_cast(message.Footer).get())) { - throw std::runtime_error("Could not set message body AMQP value."); + throw std::runtime_error("Could not set message annotations."); } - break; - case MessageBodyType::Invalid: // LCOV_EXCL_LINE - default: // LCOV_EXCL_LINE - throw std::runtime_error("Unknown message body type."); // LCOV_EXCL_LINE + } + switch (message.BodyType) + { + case MessageBodyType::None: + break; + case MessageBodyType::Data: + for (auto const& binaryVal : message.m_binaryDataBody) + { + BINARY_DATA valueData; + valueData.bytes = binaryVal.data(); + valueData.length = static_cast(binaryVal.size()); + if (message_add_body_amqp_data(rv.get(), valueData)) + { + throw std::runtime_error("Could not set message body AMQP sequence value."); + } + } + break; + case MessageBodyType::Sequence: + for (auto const& sequenceVal : message.m_amqpSequenceBody) + { + if (message_add_body_amqp_sequence(rv.get(), AmqpValue(sequenceVal))) + { + throw std::runtime_error("Could not set message body AMQP sequence value."); + } + } + break; + case MessageBodyType::Value: + if (message_set_body_amqp_value(rv.get(), message.m_amqpValueBody)) + { + throw std::runtime_error("Could not set message body AMQP value."); + } + break; + case MessageBodyType::Invalid: // LCOV_EXCL_LINE + default: // LCOV_EXCL_LINE + throw std::runtime_error("Unknown message body type."); // LCOV_EXCL_LINE + } + return rv; } - return rv; - } - std::vector AmqpMessage::GetBodyAsAmqpList() const - { - if (BodyType != MessageBodyType::Sequence) + std::vector AmqpMessage::GetBodyAsAmqpList() const { - throw std::runtime_error("Invalid body type, should be MessageBodyType::Sequence."); + if (BodyType != MessageBodyType::Sequence) + { + throw std::runtime_error("Invalid body type, should be MessageBodyType::Sequence."); + } + return m_amqpSequenceBody; } - return m_amqpSequenceBody; - } - void AmqpMessage::SetBody(AmqpBinaryData const& value) - { - BodyType = MessageBodyType::Data; - m_binaryDataBody.push_back(value); - } - void AmqpMessage::SetBody(std::vector const& value) - { - BodyType = MessageBodyType::Data; - m_binaryDataBody = value; - } - void AmqpMessage::SetBody(AmqpValue const& value) - { - BodyType = MessageBodyType::Value; - m_amqpValueBody = value; - } - void AmqpMessage::SetBody(std::vector const& value) - { - BodyType = MessageBodyType::Sequence; - m_amqpSequenceBody = value; - } - void AmqpMessage::SetBody(AmqpList const& value) - { - BodyType = MessageBodyType::Sequence; - m_amqpSequenceBody.push_back(value); - } - - AmqpValue AmqpMessage::GetBodyAsAmqpValue() const - { - if (BodyType != MessageBodyType::Value) + void AmqpMessage::SetBody(AmqpBinaryData const& value) { - throw std::runtime_error("Invalid body type, should be MessageBodyType::Value."); + BodyType = MessageBodyType::Data; + m_binaryDataBody.push_back(value); } - return m_amqpValueBody; - } - std::vector AmqpMessage::GetBodyAsBinary() const - { - if (BodyType != MessageBodyType::Data) + void AmqpMessage::SetBody(std::vector const& value) { - throw std::runtime_error("Invalid body type, should be MessageBodyType::Value."); + BodyType = MessageBodyType::Data; + m_binaryDataBody = value; } - return m_binaryDataBody; - } - - bool AmqpMessage::operator==(AmqpMessage const& that) const noexcept - { - return (Header == that.Header) && (DeliveryAnnotations == that.DeliveryAnnotations) - && (MessageAnnotations == that.MessageAnnotations) && (Properties == that.Properties) - && (ApplicationProperties == that.ApplicationProperties) && (Footer == that.Footer) - && (BodyType == that.BodyType) && (m_amqpValueBody == that.m_amqpValueBody) - && (m_amqpSequenceBody == that.m_amqpSequenceBody) - && (m_binaryDataBody == that.m_binaryDataBody); - } - - size_t AmqpMessage::GetSerializedSize(AmqpMessage const& message) - { - size_t serializedSize{}; - - serializedSize += MessageHeader::GetSerializedSize(message.Header); - serializedSize += AmqpValue::GetSerializedSize(message.MessageAnnotations); - serializedSize += MessageProperties::GetSerializedSize(message.Properties); - - // ApplicationProperties is a map of string to value, we need to convert it to an AmqpMap. + void AmqpMessage::SetBody(AmqpValue const& value) { - AmqpMap appProperties; - for (auto const& val : message.ApplicationProperties) - { - if ((val.second.GetType() == AmqpValueType::List) - || (val.second.GetType() == AmqpValueType::Map) - || (val.second.GetType() == AmqpValueType::Composite) - || (val.second.GetType() == AmqpValueType::Described)) - { - throw std::runtime_error( - "Message Application Property values must be simple value types"); - } - appProperties.emplace(val); - } - serializedSize += AmqpValue::GetSerializedSize(appProperties); + BodyType = MessageBodyType::Value; + m_amqpValueBody = value; } - - switch (message.BodyType) + void AmqpMessage::SetBody(std::vector const& value) { - default: - case MessageBodyType::Invalid: - throw std::runtime_error("Invalid message body type."); - - case MessageBodyType::Value: - serializedSize += AmqpValue::GetSerializedSize(message.m_amqpValueBody); - break; - case MessageBodyType::Data: - for (auto const& val : message.m_binaryDataBody) - { - serializedSize += AmqpValue::GetSerializedSize(val); - } - break; - case MessageBodyType::Sequence: - for (auto const& val : message.m_amqpSequenceBody) - { - serializedSize += AmqpValue::GetSerializedSize(val); - } - break; + BodyType = MessageBodyType::Sequence; + m_amqpSequenceBody = value; } - return serializedSize; - } - - std::vector AmqpMessage::Serialize(AmqpMessage const& message) - { - // size_t serializedSize = AmqpMessage::GetSerializedSize(message); - - std::vector rv; - - // Append the message Header to the serialized message. - if (message.Header.ShouldSerialize()) + void AmqpMessage::SetBody(AmqpList const& value) { - auto serializedHeader = MessageHeader::Serialize(message.Header); - rv.insert(rv.end(), serializedHeader.begin(), serializedHeader.end()); + BodyType = MessageBodyType::Sequence; + m_amqpSequenceBody.push_back(value); } - if (!message.DeliveryAnnotations.empty()) + + AmqpValue AmqpMessage::GetBodyAsAmqpValue() const { - AmqpValue deliveryAnnotations{ - amqpvalue_create_delivery_annotations(AmqpValue{message.DeliveryAnnotations})}; - auto serializedDeliveryAnnotations = AmqpValue::Serialize(deliveryAnnotations); - rv.insert( - rv.end(), serializedDeliveryAnnotations.begin(), serializedDeliveryAnnotations.end()); + if (BodyType != MessageBodyType::Value) + { + throw std::runtime_error("Invalid body type, should be MessageBodyType::Value."); + } + return m_amqpValueBody; } - if (!message.MessageAnnotations.empty()) + std::vector AmqpMessage::GetBodyAsBinary() const { - AmqpValue messageAnnotations{ - amqpvalue_create_message_annotations(AmqpValue{message.MessageAnnotations})}; - auto serializedAnnotations = AmqpValue::Serialize(messageAnnotations); - rv.insert(rv.end(), serializedAnnotations.begin(), serializedAnnotations.end()); + if (BodyType != MessageBodyType::Data) + { + throw std::runtime_error("Invalid body type, should be MessageBodyType::Value."); + } + return m_binaryDataBody; } - if (message.Properties.ShouldSerialize()) + bool AmqpMessage::operator==(AmqpMessage const& that) const noexcept { - auto serializedMessageProperties = MessageProperties::Serialize(message.Properties); - rv.insert(rv.end(), serializedMessageProperties.begin(), serializedMessageProperties.end()); + return (Header == that.Header) && (DeliveryAnnotations == that.DeliveryAnnotations) + && (MessageAnnotations == that.MessageAnnotations) && (Properties == that.Properties) + && (ApplicationProperties == that.ApplicationProperties) && (Footer == that.Footer) + && (BodyType == that.BodyType) && (m_amqpValueBody == that.m_amqpValueBody) + && (m_amqpSequenceBody == that.m_amqpSequenceBody) + && (m_binaryDataBody == that.m_binaryDataBody); } - if (!message.ApplicationProperties.empty()) + size_t AmqpMessage::GetSerializedSize(AmqpMessage const& message) { - AmqpMap appProperties; - for (auto const& val : message.ApplicationProperties) + size_t serializedSize{}; + + serializedSize += MessageHeader::GetSerializedSize(message.Header); + serializedSize += AmqpValue::GetSerializedSize(message.MessageAnnotations); + serializedSize += MessageProperties::GetSerializedSize(message.Properties); + + // ApplicationProperties is a map of string to value, we need to convert it to an AmqpMap. { - if ((val.second.GetType() == AmqpValueType::List) - || (val.second.GetType() == AmqpValueType::Map) - || (val.second.GetType() == AmqpValueType::Composite) - || (val.second.GetType() == AmqpValueType::Described)) + AmqpMap appProperties; + for (auto const& val : message.ApplicationProperties) { - throw std::runtime_error( - "Message Application Property values must be simple value types"); + if ((val.second.GetType() == AmqpValueType::List) + || (val.second.GetType() == AmqpValueType::Map) + || (val.second.GetType() == AmqpValueType::Composite) + || (val.second.GetType() == AmqpValueType::Described)) + { + throw std::runtime_error( + "Message Application Property values must be simple value types"); + } + appProperties.emplace(val); } - appProperties.emplace(val); + serializedSize += AmqpValue::GetSerializedSize(appProperties); } - AmqpValue propertiesValue{amqpvalue_create_application_properties(AmqpValue{appProperties})}; - auto serializedApplicationProperties = AmqpValue::Serialize(propertiesValue); - rv.insert( - rv.end(), serializedApplicationProperties.begin(), serializedApplicationProperties.end()); - } - switch (message.BodyType) - { - default: - case MessageBodyType::Invalid: - throw std::runtime_error("Invalid message body type."); - - case MessageBodyType::Value: { - // The message body element is an AMQP Described type, create one and serialize the - // described body. - AmqpDescribed describedBody( - static_cast(AmqpDescriptors::DataAmqpValue), message.m_amqpValueBody); - auto serializedBodyValue = AmqpValue::Serialize(describedBody); - rv.insert(rv.end(), serializedBodyValue.begin(), serializedBodyValue.end()); - } - break; - case MessageBodyType::Data: - for (auto const& val : message.m_binaryDataBody) - { - AmqpDescribed describedBody(static_cast(AmqpDescriptors::DataBinary), val); - auto serializedBodyValue = AmqpValue::Serialize(describedBody); - rv.insert(rv.end(), serializedBodyValue.begin(), serializedBodyValue.end()); - } - break; - case MessageBodyType::Sequence: { - for (auto const& val : message.m_amqpSequenceBody) - { - AmqpDescribed describedBody( - static_cast(AmqpDescriptors::DataAmqpSequence), val); - auto serializedBodyValue = AmqpValue::Serialize(describedBody); - rv.insert(rv.end(), serializedBodyValue.begin(), serializedBodyValue.end()); - } + switch (message.BodyType) + { + default: + case MessageBodyType::Invalid: + throw std::runtime_error("Invalid message body type."); + + case MessageBodyType::Value: + serializedSize += AmqpValue::GetSerializedSize(message.m_amqpValueBody); + break; + case MessageBodyType::Data: + for (auto const& val : message.m_binaryDataBody) + { + serializedSize += AmqpValue::GetSerializedSize(val); + } + break; + case MessageBodyType::Sequence: + for (auto const& val : message.m_amqpSequenceBody) + { + serializedSize += AmqpValue::GetSerializedSize(val); + } + break; } - } - if (!message.Footer.empty()) - { - AmqpValue footer{amqpvalue_create_footer(AmqpValue{message.Footer})}; - auto serializedFooter = AmqpValue::Serialize(footer); - rv.insert(rv.end(), serializedFooter.begin(), serializedFooter.end()); + return serializedSize; } - return rv; - } + std::vector AmqpMessage::Serialize(AmqpMessage const& message) + { + std::vector rv; - // The message fields, in their expected order. - std::vector expectedMessageFields{ - AmqpDescriptors::Header, - AmqpDescriptors::DeliveryAnnotations, - AmqpDescriptors::MessageAnnotations, - AmqpDescriptors::Properties, - AmqpDescriptors::ApplicationProperties}; + // Append the message Header to the serialized message. + if (message.Header.ShouldSerialize()) + { + auto serializedHeader = MessageHeader::Serialize(message.Header); + rv.insert(rv.end(), serializedHeader.begin(), serializedHeader.end()); + } + if (!message.DeliveryAnnotations.empty()) + { + AmqpValue deliveryAnnotations{ + amqpvalue_create_delivery_annotations(AmqpValue{message.DeliveryAnnotations})}; + auto serializedDeliveryAnnotations = AmqpValue::Serialize(deliveryAnnotations); + rv.insert( + rv.end(), serializedDeliveryAnnotations.begin(), serializedDeliveryAnnotations.end()); + } + if (!message.MessageAnnotations.empty()) + { + AmqpValue messageAnnotations{ + amqpvalue_create_message_annotations(AmqpValue{message.MessageAnnotations})}; + auto serializedAnnotations = AmqpValue::Serialize(messageAnnotations); + rv.insert(rv.end(), serializedAnnotations.begin(), serializedAnnotations.end()); + } - namespace { - class AmqpMessageDeserializer { - public: - AmqpMessageDeserializer() - : m_decoder{amqpvalue_decoder_create(OnAmqpMessageFieldDecodedFn, this)} + if (message.Properties.ShouldSerialize()) { + auto serializedMessageProperties = MessageProperties::Serialize(message.Properties); + rv.insert(rv.end(), serializedMessageProperties.begin(), serializedMessageProperties.end()); } - AmqpMessage operator()(std::uint8_t const* data, size_t size) const + if (!message.ApplicationProperties.empty()) { - if (amqpvalue_decode_bytes(m_decoder.get(), data, size)) + AmqpMap appProperties; + for (auto const& val : message.ApplicationProperties) { - throw std::runtime_error("Could not decode object"); + if ((val.second.GetType() == AmqpValueType::List) + || (val.second.GetType() == AmqpValueType::Map) + || (val.second.GetType() == AmqpValueType::Composite) + || (val.second.GetType() == AmqpValueType::Described)) + { + throw std::runtime_error( + "Message Application Property values must be simple value types"); + } + appProperties.emplace(val); } - return m_decodedValue; + AmqpValue propertiesValue{ + amqpvalue_create_application_properties(AmqpValue{appProperties})}; + auto serializedApplicationProperties = AmqpValue::Serialize(propertiesValue); + rv.insert( + rv.end(), + serializedApplicationProperties.begin(), + serializedApplicationProperties.end()); } - private: - UniqueAmqpDecoderHandle m_decoder; - AmqpMessage m_decodedValue; - - // Invoked on each descriptor encountered while decrypting the message. - static void OnAmqpMessageFieldDecodedFn(void* context, AMQP_VALUE value) + switch (message.BodyType) { - auto deserializer = static_cast(context); + default: + case MessageBodyType::Invalid: + throw std::runtime_error("Invalid message body type."); - deserializer->OnAmqpMessageFieldDecoded(value); + case MessageBodyType::Value: { + // The message body element is an AMQP Described type, create one and serialize the + // described body. + AmqpDescribed describedBody( + static_cast(AmqpDescriptors::DataAmqpValue), message.m_amqpValueBody); + auto serializedBodyValue = AmqpValue::Serialize(describedBody); + rv.insert(rv.end(), serializedBodyValue.begin(), serializedBodyValue.end()); + } + break; + case MessageBodyType::Data: + for (auto const& val : message.m_binaryDataBody) + { + AmqpDescribed describedBody( + static_cast(AmqpDescriptors::DataBinary), val); + auto serializedBodyValue = AmqpValue::Serialize(describedBody); + rv.insert(rv.end(), serializedBodyValue.begin(), serializedBodyValue.end()); + } + break; + case MessageBodyType::Sequence: { + for (auto const& val : message.m_amqpSequenceBody) + { + AmqpDescribed describedBody( + static_cast(AmqpDescriptors::DataAmqpSequence), val); + auto serializedBodyValue = AmqpValue::Serialize(describedBody); + rv.insert(rv.end(), serializedBodyValue.begin(), serializedBodyValue.end()); + } + } } - - void OnAmqpMessageFieldDecoded(AmqpValue value) + if (!message.Footer.empty()) { - if (value.GetType() != AmqpValueType::Described) + AmqpValue footer{amqpvalue_create_footer(AmqpValue{message.Footer})}; + auto serializedFooter = AmqpValue::Serialize(footer); + rv.insert(rv.end(), serializedFooter.begin(), serializedFooter.end()); + } + + return rv; + } + + namespace { + class AmqpMessageDeserializer { + public: + AmqpMessageDeserializer() + : m_decoder{amqpvalue_decoder_create(OnAmqpMessageFieldDecodedFn, this)} { - throw std::runtime_error("Decoded message field whose type is NOT described."); } - AmqpDescribed describedType{value.AsDescribed()}; - if (describedType.GetDescriptor().GetType() != AmqpValueType::Ulong) + + AmqpMessage operator()(std::uint8_t const* data, size_t size) const { - throw std::runtime_error("Decoded message field MUST be a LONG type."); + if (amqpvalue_decode_bytes(m_decoder.get(), data, size)) + { + throw std::runtime_error("Could not decode object"); + } + return m_decodedValue; } - switch (static_cast(static_cast(describedType.GetDescriptor()))) + + private: + UniqueAmqpDecoderHandle m_decoder; + AmqpMessage m_decodedValue; + // The message fields, in their expected order. + std::set m_expectedMessageFields{ + AmqpDescriptors::Header, + AmqpDescriptors::DeliveryAnnotations, + AmqpDescriptors::MessageAnnotations, + AmqpDescriptors::Properties, + AmqpDescriptors::ApplicationProperties, + AmqpDescriptors::DataAmqpSequence, + AmqpDescriptors::DataAmqpValue, + AmqpDescriptors::DataBinary, + AmqpDescriptors::Footer}; + + // Invoked on each descriptor encountered while decrypting the message. + static void OnAmqpMessageFieldDecodedFn(void* context, AMQP_VALUE value) { - case AmqpDescriptors::Header: { - UniqueMessageHeaderHandle messageHeader; - HEADER_HANDLE h; - if (amqpvalue_get_header(value, &h)) - { - throw std::runtime_error("Could not convert field to header."); - } - messageHeader.reset(h); - h = nullptr; - m_decodedValue.Header = _internal::MessageHeaderFactory::FromUamqp(messageHeader); - break; + auto deserializer = static_cast(context); + + deserializer->OnAmqpMessageFieldDecoded(value); + } + + // Invoked when a message field + void OnAmqpMessageFieldDecoded(AmqpValue value) + { + if (value.GetType() != AmqpValueType::Described) + { + throw std::runtime_error("Decoded message field whose type is NOT described."); } - case AmqpDescriptors::DeliveryAnnotations: - m_decodedValue.DeliveryAnnotations = describedType.GetValue().AsMap(); - break; - case AmqpDescriptors::MessageAnnotations: - m_decodedValue.MessageAnnotations = describedType.GetValue().AsMap(); - break; - case AmqpDescriptors::Properties: { - UniquePropertiesHandle properties; - PROPERTIES_HANDLE h; - if (amqpvalue_get_properties(value, &h)) + AmqpDescribed describedType(value.AsDescribed()); + if (describedType.GetDescriptor().GetType() != AmqpValueType::Ulong) + { + throw std::runtime_error("Decoded message field MUST be a LONG type."); + } + + AmqpDescriptors fieldDescriptor( + static_cast(static_cast(describedType.GetDescriptor()))); + if (m_expectedMessageFields.find(fieldDescriptor) == m_expectedMessageFields.end()) + { + throw std::runtime_error("Found message field is not in the set of expected fields."); + } + else + { + // Once we've seen a field, we can remove that field from the set of expected fields, + // and also the fields which must come before it. + // + // The two exceptions are the DataBinary and DataAmqpSequence fields, which can have + // more than one instance. + switch (fieldDescriptor) { - throw std::runtime_error("Could not convert field to header."); + case AmqpDescriptors::Header: + m_expectedMessageFields.erase(AmqpDescriptors::Header); + break; + case AmqpDescriptors::DeliveryAnnotations: + // Once we've seen a DeliveryAnnotations, we no longer expect to see a Header or + // another DeliveryAnnotations. + m_expectedMessageFields.erase(AmqpDescriptors::Header); + m_expectedMessageFields.erase(AmqpDescriptors::DeliveryAnnotations); + break; + case AmqpDescriptors::MessageAnnotations: + // Once we've seen a MessageAnnotations, we no longer expect to see a Header, + // DeliveryAnnotations, or a MessageAnnotations. + m_expectedMessageFields.erase(AmqpDescriptors::Header); + m_expectedMessageFields.erase(AmqpDescriptors::DeliveryAnnotations); + m_expectedMessageFields.erase(AmqpDescriptors::MessageAnnotations); + break; + case AmqpDescriptors::Properties: + m_expectedMessageFields.erase(AmqpDescriptors::Header); + m_expectedMessageFields.erase(AmqpDescriptors::DeliveryAnnotations); + m_expectedMessageFields.erase(AmqpDescriptors::MessageAnnotations); + m_expectedMessageFields.erase(AmqpDescriptors::Properties); + break; + case AmqpDescriptors::ApplicationProperties: + m_expectedMessageFields.erase(AmqpDescriptors::Header); + m_expectedMessageFields.erase(AmqpDescriptors::DeliveryAnnotations); + m_expectedMessageFields.erase(AmqpDescriptors::MessageAnnotations); + m_expectedMessageFields.erase(AmqpDescriptors::Properties); + m_expectedMessageFields.erase(AmqpDescriptors::ApplicationProperties); + break; + case AmqpDescriptors::DataAmqpSequence: + m_expectedMessageFields.erase(AmqpDescriptors::Header); + m_expectedMessageFields.erase(AmqpDescriptors::DeliveryAnnotations); + m_expectedMessageFields.erase(AmqpDescriptors::MessageAnnotations); + m_expectedMessageFields.erase(AmqpDescriptors::Properties); + m_expectedMessageFields.erase(AmqpDescriptors::ApplicationProperties); + // When we see an DataAmqpSequence, we no longer expect to see any other data type. + m_expectedMessageFields.erase(AmqpDescriptors::DataAmqpValue); + m_expectedMessageFields.erase(AmqpDescriptors::DataBinary); + break; + case AmqpDescriptors::DataAmqpValue: + m_expectedMessageFields.erase(AmqpDescriptors::Header); + m_expectedMessageFields.erase(AmqpDescriptors::DeliveryAnnotations); + m_expectedMessageFields.erase(AmqpDescriptors::MessageAnnotations); + m_expectedMessageFields.erase(AmqpDescriptors::Properties); + m_expectedMessageFields.erase(AmqpDescriptors::ApplicationProperties); + // When we see an DataAmqpValue, we no longer expect to see any other data type. + m_expectedMessageFields.erase(AmqpDescriptors::DataAmqpValue); + m_expectedMessageFields.erase(AmqpDescriptors::DataAmqpSequence); + m_expectedMessageFields.erase(AmqpDescriptors::DataBinary); + break; + case AmqpDescriptors::DataBinary: + m_expectedMessageFields.erase(AmqpDescriptors::Header); + m_expectedMessageFields.erase(AmqpDescriptors::DeliveryAnnotations); + m_expectedMessageFields.erase(AmqpDescriptors::MessageAnnotations); + m_expectedMessageFields.erase(AmqpDescriptors::Properties); + m_expectedMessageFields.erase(AmqpDescriptors::ApplicationProperties); + // When we see an DataBinary, we no longer expect to see any other data type. + m_expectedMessageFields.erase(AmqpDescriptors::DataAmqpValue); + m_expectedMessageFields.erase(AmqpDescriptors::DataAmqpSequence); + break; + case AmqpDescriptors::Footer: + m_expectedMessageFields.erase(AmqpDescriptors::Header); + m_expectedMessageFields.erase(AmqpDescriptors::DeliveryAnnotations); + m_expectedMessageFields.erase(AmqpDescriptors::MessageAnnotations); + m_expectedMessageFields.erase(AmqpDescriptors::Properties); + m_expectedMessageFields.erase(AmqpDescriptors::ApplicationProperties); + // When we see an DataBinary, we no longer expect to see any other data type. + m_expectedMessageFields.erase(AmqpDescriptors::DataBinary); + m_expectedMessageFields.erase(AmqpDescriptors::DataAmqpValue); + m_expectedMessageFields.erase(AmqpDescriptors::DataAmqpSequence); + m_expectedMessageFields.erase(AmqpDescriptors::Footer); + break; } - properties.reset(h); - h = nullptr; - m_decodedValue.Properties = _internal::MessagePropertiesFactory::FromUamqp(properties); - break; } - case AmqpDescriptors::ApplicationProperties: { - auto propertyMap = describedType.GetValue().AsMap(); - for (auto const& val : propertyMap) - { - if (val.first.GetType() != AmqpValueType::String) + + switch (fieldDescriptor) + { + case AmqpDescriptors::Header: { + UniqueMessageHeaderHandle messageHeader; + HEADER_HANDLE h; + if (amqpvalue_get_header(value, &h)) { - throw std::runtime_error("Key of applications properties must be a string."); + throw std::runtime_error("Could not convert field to header."); } - if ((val.second.GetType() == AmqpValueType::List) - || (val.second.GetType() == AmqpValueType::Map) - || (val.second.GetType() == AmqpValueType::Composite) - || (val.second.GetType() == AmqpValueType::Described)) + messageHeader.reset(h); + h = nullptr; + m_decodedValue.Header = _internal::MessageHeaderFactory::FromUamqp(messageHeader); + break; + } + case AmqpDescriptors::DeliveryAnnotations: + m_decodedValue.DeliveryAnnotations = describedType.GetValue().AsMap(); + break; + case AmqpDescriptors::MessageAnnotations: + m_decodedValue.MessageAnnotations = describedType.GetValue().AsMap(); + break; + case AmqpDescriptors::Properties: { + UniquePropertiesHandle properties; + PROPERTIES_HANDLE h; + if (amqpvalue_get_properties(value, &h)) { - throw std::runtime_error( - "Message Application Property values must be simple value types"); + throw std::runtime_error("Could not convert field to header."); } - m_decodedValue.ApplicationProperties.emplace( - static_cast(val.first), val.second); + properties.reset(h); + h = nullptr; + m_decodedValue.Properties + = _internal::MessagePropertiesFactory::FromUamqp(properties); + break; } - break; + case AmqpDescriptors::ApplicationProperties: { + auto propertyMap = describedType.GetValue().AsMap(); + for (auto const& val : propertyMap) + { + if (val.first.GetType() != AmqpValueType::String) + { + throw std::runtime_error("Key of applications properties must be a string."); + } + if ((val.second.GetType() == AmqpValueType::List) + || (val.second.GetType() == AmqpValueType::Map) + || (val.second.GetType() == AmqpValueType::Composite) + || (val.second.GetType() == AmqpValueType::Described)) + { + throw std::runtime_error( + "Message Application Property values must be simple value types"); + } + m_decodedValue.ApplicationProperties.emplace( + static_cast(val.first), val.second); + } + break; + } + case AmqpDescriptors::DataAmqpValue: + m_decodedValue.SetBody(describedType.GetValue()); + break; + case AmqpDescriptors::DataAmqpSequence: + m_decodedValue.SetBody(describedType.GetValue().AsList()); + break; + case AmqpDescriptors::DataBinary: + // Each call to SetBody will append the binary value to the vector of binary bodies. + m_decodedValue.SetBody(describedType.GetValue().AsBinary()); + break; + case AmqpDescriptors::Footer: + m_decodedValue.Footer = describedType.GetValue().AsMap(); + break; + default: + throw std::runtime_error("Unknown message descriptor."); } - case AmqpDescriptors::DataAmqpValue: - m_decodedValue.SetBody(describedType.GetValue()); - break; - case AmqpDescriptors::DataAmqpSequence: - m_decodedValue.SetBody(describedType.GetValue().AsList()); - break; - case AmqpDescriptors::DataBinary: - // Each call to SetBody will append the binary value to the vector of binary bodies. - m_decodedValue.SetBody(describedType.GetValue().AsBinary()); - break; - case AmqpDescriptors::Footer: - m_decodedValue.Footer = describedType.GetValue().AsMap(); - break; - default: - throw std::runtime_error("Unknown message descriptor."); } - } - }; - } // namespace + }; + } // namespace - AmqpMessage AmqpMessage::Deserialize(std::uint8_t const* buffer, size_t size) - { - return AmqpMessageDeserializer{}(buffer, size); - } + AmqpMessage AmqpMessage::Deserialize(std::uint8_t const* buffer, size_t size) + { + return AmqpMessageDeserializer{}(buffer, size); + } - std::ostream& operator<<(std::ostream& os, AmqpMessage const& message) - { - os << "Message: " << std::endl; - os << "Header " << message.Header << std::endl; - os << "Properties: " << message.Properties; - os << "Body: [" << std::endl; - switch (message.BodyType) + std::ostream& operator<<(std::ostream& os, AmqpMessage const& message) { - case MessageBodyType::Invalid: // LCOV_EXCL_LINE - os << "Invalid"; // LCOV_EXCL_LINE - break; // LCOV_EXCL_LINE - case MessageBodyType::None: - os << "None"; - break; - case MessageBodyType::Data: { - os << "AMQP Data: ["; - auto bodyBinary = message.GetBodyAsBinary(); - uint8_t i = 0; - for (auto const& val : bodyBinary) - { - os << "Data: " << val << std::endl; - if (i < bodyBinary.size() - 1) + os << "Message: " << std::endl; + os << "Header " << message.Header << std::endl; + os << "Properties: " << message.Properties; + os << "Body: [" << std::endl; + switch (message.BodyType) + { + case MessageBodyType::Invalid: // LCOV_EXCL_LINE + os << "Invalid"; // LCOV_EXCL_LINE + break; // LCOV_EXCL_LINE + case MessageBodyType::None: + os << "None"; + break; + case MessageBodyType::Data: { + os << "AMQP Data: ["; + auto bodyBinary = message.GetBodyAsBinary(); + uint8_t i = 0; + for (auto const& val : bodyBinary) { - os << ", "; + os << "Data: " << val << std::endl; + if (i < bodyBinary.size() - 1) + { + os << ", "; + } + i += 1; } - i += 1; + os << "]"; } - os << "]"; - } - break; - case MessageBodyType::Sequence: { - os << "AMQP Sequence: ["; - auto bodySequence = message.GetBodyAsAmqpList(); - uint8_t i = 0; - for (auto const& val : bodySequence) - { - os << "Sequence: " << val << std::endl; - if (i < bodySequence.size() - 1) + break; + case MessageBodyType::Sequence: { + os << "AMQP Sequence: ["; + auto bodySequence = message.GetBodyAsAmqpList(); + uint8_t i = 0; + for (auto const& val : bodySequence) { - os << ", "; + os << "Sequence: " << val << std::endl; + if (i < bodySequence.size() - 1) + { + os << ", "; + } + i += 1; } - i += 1; + os << "]"; } - os << "]"; - } - break; - case MessageBodyType::Value: - os << "AmqpValue: " << message.GetBodyAsAmqpValue(); break; - } - os << "]"; + case MessageBodyType::Value: + os << "AmqpValue: " << message.GetBodyAsAmqpValue(); + break; + } + os << "]"; - { - if (!message.ApplicationProperties.empty()) { - os << std::endl << "Application Properties: "; - for (auto const& val : message.ApplicationProperties) + if (!message.ApplicationProperties.empty()) { - os << "{" << val.first << ", " << val.second << "}"; + os << std::endl << "Application Properties: "; + for (auto const& val : message.ApplicationProperties) + { + os << "{" << val.first << ", " << val.second << "}"; + } } } - } - if (!message.DeliveryAnnotations.empty()) - { - os << std::endl << "Delivery Annotations: "; - for (auto const& val : message.DeliveryAnnotations) + if (!message.DeliveryAnnotations.empty()) { - os << "{" << val.first << ", " << val.second << "}"; + os << std::endl << "Delivery Annotations: "; + for (auto const& val : message.DeliveryAnnotations) + { + os << "{" << val.first << ", " << val.second << "}"; + } } - } - if (!message.MessageAnnotations.empty()) - { - os << std::endl << "Message Annotations: "; - for (auto const& val : message.MessageAnnotations) + if (!message.MessageAnnotations.empty()) { - os << "{" << val.first << ", " << val.second << "}"; + os << std::endl << "Message Annotations: "; + for (auto const& val : message.MessageAnnotations) + { + os << "{" << val.first << ", " << val.second << "}"; + } } - } - if (!message.Footer.empty()) - { - os << "Footer: "; - for (auto const& val : message.Footer) + if (!message.Footer.empty()) { - os << "{" << val.first << ", " << val.second << "}"; + os << "Footer: "; + for (auto const& val : message.Footer) + { + os << "{" << val.first << ", " << val.second << "}"; + } } + return os; } - return os; - } }}}} // namespace Azure::Core::Amqp::Models diff --git a/sdk/core/azure-core-amqp/test/ut/amqp_value_tests.cpp b/sdk/core/azure-core-amqp/test/ut/amqp_value_tests.cpp index f835f0600f..05f1a9aeae 100644 --- a/sdk/core/azure-core-amqp/test/ut/amqp_value_tests.cpp +++ b/sdk/core/azure-core-amqp/test/ut/amqp_value_tests.cpp @@ -891,7 +891,7 @@ TEST_F(TestValueSerialization, SerializeBinary) AmqpValue value{AmqpValue::Deserialize(testVector.data(), testVector.size())}; EXPECT_EQ(value.GetType(), AmqpValueType::Binary); EXPECT_EQ(value.AsBinary().size(), 16); - auto binary{value.AsBinary()}; + auto binary(value.AsBinary()); std::array valueAsArray; std::copy_n(binary.begin(), 16, valueAsArray.begin()); @@ -908,7 +908,7 @@ TEST_F(TestValueSerialization, SerializeBinary) AmqpValue value{AmqpValue::Deserialize(testVector.data(), testVector.size())}; EXPECT_EQ(value.GetType(), AmqpValueType::Binary); EXPECT_EQ(value.AsBinary().size(), 16); - auto binary{value.AsBinary()}; + auto binary(value.AsBinary()); std::array valueAsArray; std::copy_n(binary.begin(), 16, valueAsArray.begin()); @@ -1283,7 +1283,7 @@ TEST_F(TestValueSerialization, SerializeArray) std::vector testVector{0xe0, 0x01, 0x00}; AmqpValue value{AmqpValue::Deserialize(testVector.data(), testVector.size())}; EXPECT_EQ(value.GetType(), AmqpValueType::Array); - AmqpArray array{value.AsArray()}; + AmqpArray array(value.AsArray()); EXPECT_EQ(array.size(), 0); std::vector val = AmqpValue::Serialize(value); @@ -1294,7 +1294,7 @@ TEST_F(TestValueSerialization, SerializeArray) std::vector testVector{0xf0, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00}; AmqpValue value{AmqpValue::Deserialize(testVector.data(), testVector.size())}; EXPECT_EQ(value.GetType(), AmqpValueType::Array); - AmqpArray array{value.AsArray()}; + AmqpArray array(value.AsArray()); EXPECT_EQ(array.size(), 0); std::vector val = AmqpValue::Serialize(value); @@ -1336,7 +1336,7 @@ TEST_F(TestValueSerialization, SerializeArray) AmqpValue value{AmqpValue::Deserialize(testVector.data(), testVector.size())}; EXPECT_EQ(value.GetType(), AmqpValueType::Array); - AmqpArray array{value.AsArray()}; + AmqpArray array(value.AsArray()); EXPECT_EQ(array.size(), values.size()); EXPECT_TRUE(std::equal(values.begin(), values.end(), array.begin(), array.end())); @@ -1378,7 +1378,7 @@ TEST_F(TestValueSerialization, SerializeArray) AmqpValue value{AmqpValue::Deserialize(testVector.data(), testVector.size())}; EXPECT_EQ(value.GetType(), AmqpValueType::Array); - AmqpArray array{value.AsArray()}; + AmqpArray array(value.AsArray()); EXPECT_EQ(array.size(), values.size()); size_t i = 0; for (auto const& val : array) @@ -1410,7 +1410,7 @@ TEST_F(TestValueSerialization, SerializeMap) std::vector testVector{0xc1, 0x01, 0x00}; AmqpValue value{AmqpValue::Deserialize(testVector.data(), testVector.size())}; EXPECT_EQ(value.GetType(), AmqpValueType::Map); - AmqpMap map{value.AsMap()}; + AmqpMap map(value.AsMap()); EXPECT_EQ(map.size(), 0); std::vector val = AmqpValue::Serialize(value); @@ -1422,7 +1422,7 @@ TEST_F(TestValueSerialization, SerializeMap) std::vector testVector{0xd1, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00}; AmqpValue value{AmqpValue::Deserialize(testVector.data(), testVector.size())}; EXPECT_EQ(value.GetType(), AmqpValueType::Map); - AmqpMap map{value.AsMap()}; + AmqpMap map(value.AsMap()); EXPECT_EQ(map.size(), 0); std::vector val = AmqpValue::Serialize(value); @@ -1471,7 +1471,7 @@ TEST_F(TestValueSerialization, SerializeMap) AmqpValue value{AmqpValue::Deserialize(testVector.data(), testVector.size())}; EXPECT_EQ(value.GetType(), AmqpValueType::Map); - AmqpMap map{value.AsMap()}; + AmqpMap map(value.AsMap()); EXPECT_EQ(map.size(), values.size()); EXPECT_TRUE(std::equal(values.begin(), values.end(), map.begin(), map.end())); @@ -1519,7 +1519,7 @@ TEST_F(TestValueSerialization, SerializeMap) AmqpValue value{AmqpValue::Deserialize(testVector.data(), testVector.size())}; EXPECT_EQ(value.GetType(), AmqpValueType::Map); - AmqpMap map{value.AsMap()}; + AmqpMap map(value.AsMap()); EXPECT_EQ(map.size(), values.size()); int i = 0; auto valIterator = values.begin(); From cbdc4fbc3f316fc769e8e880f90774ace9548dc3 Mon Sep 17 00:00:00 2001 From: Larry Osterman Date: Thu, 27 Apr 2023 14:16:36 -0700 Subject: [PATCH 11/17] clang fix 6 --- sdk/core/azure-core-amqp/src/models/amqp_message.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/sdk/core/azure-core-amqp/src/models/amqp_message.cpp b/sdk/core/azure-core-amqp/src/models/amqp_message.cpp index ac1c736318..8511df278e 100644 --- a/sdk/core/azure-core-amqp/src/models/amqp_message.cpp +++ b/sdk/core/azure-core-amqp/src/models/amqp_message.cpp @@ -683,6 +683,8 @@ namespace Azure { namespace Core { namespace Amqp { namespace Models { m_expectedMessageFields.erase(AmqpDescriptors::DataAmqpSequence); m_expectedMessageFields.erase(AmqpDescriptors::Footer); break; + default: + throw std::runtime_error("Unknown message descriptor."); } } @@ -753,8 +755,8 @@ namespace Azure { namespace Core { namespace Amqp { namespace Models { case AmqpDescriptors::Footer: m_decodedValue.Footer = describedType.GetValue().AsMap(); break; - default: - throw std::runtime_error("Unknown message descriptor."); + default: // LCOV_EXCL_LINE + throw std::runtime_error("Unknown message descriptor."); // LCOV_EXCL_LINE } } }; From 1c9789823e536bee440938dff92072aee33e4173 Mon Sep 17 00:00:00 2001 From: Larry Osterman Date: Thu, 27 Apr 2023 14:24:02 -0700 Subject: [PATCH 12/17] Pull request suggestions --- .../azure/core/amqp/models/amqp_message.hpp | 28 +++++++++---------- .../inc/azure/core/amqp/models/amqp_value.hpp | 1 + .../test/ut/amqp_message_tests.cpp | 2 ++ .../test/ut/amqp_value_tests.cpp | 13 +++++++++ 4 files changed, 30 insertions(+), 14 deletions(-) diff --git a/sdk/core/azure-core-amqp/inc/azure/core/amqp/models/amqp_message.hpp b/sdk/core/azure-core-amqp/inc/azure/core/amqp/models/amqp_message.hpp index c20d062502..2cb448d689 100644 --- a/sdk/core/azure-core-amqp/inc/azure/core/amqp/models/amqp_message.hpp +++ b/sdk/core/azure-core-amqp/inc/azure/core/amqp/models/amqp_message.hpp @@ -63,6 +63,7 @@ namespace Azure { namespace Core { namespace Amqp { namespace Models { AmqpMessage& operator=(AmqpMessage&&) noexcept = default; bool operator==(AmqpMessage const& other) const noexcept; + bool operator!=(AmqpMessage const& other) const noexcept { return !(*this == other); } AmqpMessage(std::nullptr_t) : m_hasValue{false} {} operator bool() const noexcept { return m_hasValue; } @@ -258,21 +259,20 @@ namespace Azure { namespace Core { namespace Amqp { namespace Models { bool m_hasValue{true}; // By default, an AmqpMessage has a value. }; std::ostream& operator<<(std::ostream&, AmqpMessage const&); - }}}} // namespace Azure::Core::Amqp::Models namespace Azure { namespace Core { namespace Amqp { namespace Models { namespace _internal { - /** - * @brief uAMQP interoperability functions to convert a MessageProperties to a uAMQP - * PROPERTIES_HANDLE and back. - * - * @remarks This class should not be used directly. It is used by the uAMQP interoperability - * layer. - */ - class AmqpMessageFactory { - public: - static AmqpMessage FromUamqp(UniqueMessageHandle const& properties); - static AmqpMessage FromUamqp(MESSAGE_INSTANCE_TAG* properties); - static UniqueMessageHandle ToUamqp(AmqpMessage const& properties); - }; + /** + * @brief uAMQP interoperability functions to convert a MessageProperties to a uAMQP + * PROPERTIES_HANDLE and back. + * + * @remarks This class should not be used directly. It is used by the uAMQP interoperability + * layer. + */ + class AmqpMessageFactory { + public: + static AmqpMessage FromUamqp(UniqueMessageHandle const& properties); + static AmqpMessage FromUamqp(MESSAGE_INSTANCE_TAG* properties); + static UniqueMessageHandle ToUamqp(AmqpMessage const& properties); + }; }}}}} // namespace Azure::Core::Amqp::Models::_internal diff --git a/sdk/core/azure-core-amqp/inc/azure/core/amqp/models/amqp_value.hpp b/sdk/core/azure-core-amqp/inc/azure/core/amqp/models/amqp_value.hpp index 7e0b14629b..c4e3faf3dc 100644 --- a/sdk/core/azure-core-amqp/inc/azure/core/amqp/models/amqp_value.hpp +++ b/sdk/core/azure-core-amqp/inc/azure/core/amqp/models/amqp_value.hpp @@ -541,6 +541,7 @@ namespace Azure { namespace Core { namespace Amqp { namespace Models { } bool operator<(ThisType const& that) const { return m_value < that.m_value; } bool operator==(ThisType const& that) const { return m_value == that.m_value; } + bool operator!=(ThisType const& that) const { return m_value != that.m_value; } bool empty() const noexcept { return m_value.empty(); } /** diff --git a/sdk/core/azure-core-amqp/test/ut/amqp_message_tests.cpp b/sdk/core/azure-core-amqp/test/ut/amqp_message_tests.cpp index 9ff9236969..3ff6ce6a54 100644 --- a/sdk/core/azure-core-amqp/test/ut/amqp_message_tests.cpp +++ b/sdk/core/azure-core-amqp/test/ut/amqp_message_tests.cpp @@ -25,9 +25,11 @@ TEST_F(TestMessage, SimpleCreate) AmqpMessage message3(message2); AmqpMessage message4; message4 = message2; + EXPECT_EQ(message4, message2); GTEST_LOG_(INFO) << message4; AmqpMessage message5 = std::move(message3); GTEST_LOG_(INFO) << message5; + EXPECT_NE(message5, message3); } { diff --git a/sdk/core/azure-core-amqp/test/ut/amqp_value_tests.cpp b/sdk/core/azure-core-amqp/test/ut/amqp_value_tests.cpp index 05f1a9aeae..7b40594363 100644 --- a/sdk/core/azure-core-amqp/test/ut/amqp_value_tests.cpp +++ b/sdk/core/azure-core-amqp/test/ut/amqp_value_tests.cpp @@ -135,6 +135,19 @@ TEST_F(TestValues, SimpleCreate) EXPECT_EQ(uuid.ToString(), static_cast(value).ToString()); EXPECT_TRUE(AmqpValue() < value); } + + { + AmqpValue value1{29}; + AmqpValue value2(std::move(value1)); + AmqpValue value3(value2); + AmqpValue value4; + value4 = value2; + EXPECT_EQ(value4, value2); + GTEST_LOG_(INFO) << value4; + AmqpValue value5 = std::move(value3); + GTEST_LOG_(INFO) << value5; + EXPECT_NE(value5, value3); + } } TEST_F(TestValues, TestBinary) From 51120f78ff61e65989d6fbc67414dab7c7f90b42 Mon Sep 17 00:00:00 2001 From: Larry Osterman Date: Thu, 27 Apr 2023 14:24:39 -0700 Subject: [PATCH 13/17] clang-format --- .../azure/core/amqp/models/amqp_message.hpp | 26 +- .../src/models/amqp_message.cpp | 1312 ++++++++--------- .../test/ut/amqp_value_tests.cpp | 2 +- 3 files changed, 666 insertions(+), 674 deletions(-) diff --git a/sdk/core/azure-core-amqp/inc/azure/core/amqp/models/amqp_message.hpp b/sdk/core/azure-core-amqp/inc/azure/core/amqp/models/amqp_message.hpp index 2cb448d689..83a8a65ac6 100644 --- a/sdk/core/azure-core-amqp/inc/azure/core/amqp/models/amqp_message.hpp +++ b/sdk/core/azure-core-amqp/inc/azure/core/amqp/models/amqp_message.hpp @@ -262,17 +262,17 @@ namespace Azure { namespace Core { namespace Amqp { namespace Models { }}}} // namespace Azure::Core::Amqp::Models namespace Azure { namespace Core { namespace Amqp { namespace Models { namespace _internal { - /** - * @brief uAMQP interoperability functions to convert a MessageProperties to a uAMQP - * PROPERTIES_HANDLE and back. - * - * @remarks This class should not be used directly. It is used by the uAMQP interoperability - * layer. - */ - class AmqpMessageFactory { - public: - static AmqpMessage FromUamqp(UniqueMessageHandle const& properties); - static AmqpMessage FromUamqp(MESSAGE_INSTANCE_TAG* properties); - static UniqueMessageHandle ToUamqp(AmqpMessage const& properties); - }; + /** + * @brief uAMQP interoperability functions to convert a MessageProperties to a uAMQP + * PROPERTIES_HANDLE and back. + * + * @remarks This class should not be used directly. It is used by the uAMQP interoperability + * layer. + */ + class AmqpMessageFactory { + public: + static AmqpMessage FromUamqp(UniqueMessageHandle const& properties); + static AmqpMessage FromUamqp(MESSAGE_INSTANCE_TAG* properties); + static UniqueMessageHandle ToUamqp(AmqpMessage const& properties); + }; }}}}} // namespace Azure::Core::Amqp::Models::_internal diff --git a/sdk/core/azure-core-amqp/src/models/amqp_message.cpp b/sdk/core/azure-core-amqp/src/models/amqp_message.cpp index 8511df278e..7de6837ad8 100644 --- a/sdk/core/azure-core-amqp/src/models/amqp_message.cpp +++ b/sdk/core/azure-core-amqp/src/models/amqp_message.cpp @@ -24,835 +24,827 @@ using namespace Azure::Core::Amqp::_detail; namespace Azure { namespace Core { namespace Amqp { namespace Models { - namespace { + namespace { - UniqueMessageHeaderHandle GetHeaderFromMessage(MESSAGE_HANDLE message) + UniqueMessageHeaderHandle GetHeaderFromMessage(MESSAGE_HANDLE message) + { + if (message != nullptr) { - if (message != nullptr) + HEADER_HANDLE headerValue; + if (!message_get_header(message, &headerValue)) { - HEADER_HANDLE headerValue; - if (!message_get_header(message, &headerValue)) - { - return Azure::Core::_internal::UniqueHandle(headerValue); - } + return Azure::Core::_internal::UniqueHandle(headerValue); } - return nullptr; } + return nullptr; + } - UniquePropertiesHandle GetPropertiesFromMessage(MESSAGE_HANDLE const& message) + UniquePropertiesHandle GetPropertiesFromMessage(MESSAGE_HANDLE const& message) + { + if (message != nullptr) { - if (message != nullptr) + PROPERTIES_HANDLE propertiesValue; + if (!message_get_properties(message, &propertiesValue)) { - PROPERTIES_HANDLE propertiesValue; - if (!message_get_properties(message, &propertiesValue)) - { - return Azure::Core::_internal::UniqueHandle(propertiesValue); - } + return Azure::Core::_internal::UniqueHandle(propertiesValue); } - return nullptr; } - } // namespace + return nullptr; + } + } // namespace + + AmqpMessage _internal::AmqpMessageFactory::FromUamqp(UniqueMessageHandle const& message) + { + return FromUamqp(message.get()); + } - AmqpMessage _internal::AmqpMessageFactory::FromUamqp(UniqueMessageHandle const& message) + AmqpMessage _internal::AmqpMessageFactory::FromUamqp(MESSAGE_HANDLE message) + { + if (message == nullptr) { - return FromUamqp(message.get()); + return AmqpMessage(nullptr); } + AmqpMessage rv; + rv.Header = _internal::MessageHeaderFactory::FromUamqp(GetHeaderFromMessage(message)); + rv.Properties + = _internal::MessagePropertiesFactory::FromUamqp(GetPropertiesFromMessage(message)); - AmqpMessage _internal::AmqpMessageFactory::FromUamqp(MESSAGE_HANDLE message) { - if (message == nullptr) + delivery_annotations annotationsVal; + // message_get_delivery_annotations returns a clone of the message annotations. + if (!message_get_delivery_annotations(message, &annotationsVal) && annotationsVal != nullptr) { - return AmqpMessage(nullptr); - } - AmqpMessage rv; - rv.Header = _internal::MessageHeaderFactory::FromUamqp(GetHeaderFromMessage(message)); - rv.Properties - = _internal::MessagePropertiesFactory::FromUamqp(GetPropertiesFromMessage(message)); - - { - delivery_annotations annotationsVal; - // message_get_delivery_annotations returns a clone of the message annotations. - if (!message_get_delivery_annotations(message, &annotationsVal) - && annotationsVal != nullptr) - { - UniqueAmqpValueHandle deliveryAnnotations(annotationsVal); - auto deliveryMap = AmqpValue{deliveryAnnotations.get()}.AsMap(); - rv.DeliveryAnnotations = deliveryMap; - } + UniqueAmqpValueHandle deliveryAnnotations(annotationsVal); + auto deliveryMap = AmqpValue{deliveryAnnotations.get()}.AsMap(); + rv.DeliveryAnnotations = deliveryMap; } + } + { + // message_get_message_annotations returns a clone of the message annotations. + AMQP_VALUE annotationVal; + if (!message_get_message_annotations(message, &annotationVal) && annotationVal) { - // message_get_message_annotations returns a clone of the message annotations. - AMQP_VALUE annotationVal; - if (!message_get_message_annotations(message, &annotationVal) && annotationVal) + UniqueAmqpValueHandle messageAnnotations(annotationVal); + if (messageAnnotations) { - UniqueAmqpValueHandle messageAnnotations(annotationVal); - if (messageAnnotations) - { - auto messageMap = AmqpValue{messageAnnotations.get()}.AsMap(); - rv.MessageAnnotations = messageMap; - } + auto messageMap = AmqpValue{messageAnnotations.get()}.AsMap(); + rv.MessageAnnotations = messageMap; } } - { - /* - * The ApplicationProperties field in an AMQP message for uAMQP expects that the map value - * is wrapped as a described value. A described value has a ULONG descriptor value and a - * value type. - * - * Making things even more interesting, the ApplicationProperties field in an uAMQP message - * is asymmetric. - * - * The MessageSender class will wrap ApplicationProperties in a described value, so when - * setting application properties, the described value must NOT be present, but when - * decoding an application properties, the GetApplicationProperties method has to be able to - * handle both when the described value is present or not. - */ - AMQP_VALUE properties; - if (!message_get_application_properties(message, &properties) && properties) + } + { + /* + * The ApplicationProperties field in an AMQP message for uAMQP expects that the map value + * is wrapped as a described value. A described value has a ULONG descriptor value and a + * value type. + * + * Making things even more interesting, the ApplicationProperties field in an uAMQP message + * is asymmetric. + * + * The MessageSender class will wrap ApplicationProperties in a described value, so when + * setting application properties, the described value must NOT be present, but when + * decoding an application properties, the GetApplicationProperties method has to be able to + * handle both when the described value is present or not. + */ + AMQP_VALUE properties; + if (!message_get_application_properties(message, &properties) && properties) + { + UniqueAmqpValueHandle describedProperties(properties); + properties = nullptr; + if (describedProperties) { - UniqueAmqpValueHandle describedProperties(properties); - properties = nullptr; - if (describedProperties) + AMQP_VALUE value; + if (amqpvalue_get_type(describedProperties.get()) == AMQP_TYPE_DESCRIBED) { - AMQP_VALUE value; - if (amqpvalue_get_type(describedProperties.get()) == AMQP_TYPE_DESCRIBED) + auto describedType = amqpvalue_get_inplace_descriptor(describedProperties.get()); + uint64_t describedTypeValue; + if (amqpvalue_get_ulong(describedType, &describedTypeValue)) { - auto describedType = amqpvalue_get_inplace_descriptor(describedProperties.get()); - uint64_t describedTypeValue; - if (amqpvalue_get_ulong(describedType, &describedTypeValue)) - { - throw std::runtime_error( - "Could not retrieve application properties described type."); - } - if (describedTypeValue - != static_cast( - Azure::Core::Amqp::_detail::AmqpDescriptors::ApplicationProperties)) - { - throw std::runtime_error( - "Application Properties are not the corect described type."); - } - - value = amqpvalue_get_inplace_described_value(describedProperties.get()); + throw std::runtime_error("Could not retrieve application properties described type."); } - else + if (describedTypeValue + != static_cast( + Azure::Core::Amqp::_detail::AmqpDescriptors::ApplicationProperties)) { - value = describedProperties.get(); + throw std::runtime_error("Application Properties are not the corect described type."); } - if (amqpvalue_get_type(value) != AMQP_TYPE_MAP) + + value = amqpvalue_get_inplace_described_value(describedProperties.get()); + } + else + { + value = describedProperties.get(); + } + if (amqpvalue_get_type(value) != AMQP_TYPE_MAP) + { + throw std::runtime_error("Application Properties must be a map?!"); + } + auto appProperties = AmqpMap(value); + for (auto const& val : appProperties) + { + if (val.first.GetType() != AmqpValueType::String) { - throw std::runtime_error("Application Properties must be a map?!"); - } - auto appProperties = AmqpMap(value); - for (auto const& val : appProperties) - { - if (val.first.GetType() != AmqpValueType::String) - { - throw std::runtime_error("Key of Application Properties must be a string."); - } - rv.ApplicationProperties.emplace( - std::make_pair(static_cast(val.first), val.second)); + throw std::runtime_error("Key of Application Properties must be a string."); } + rv.ApplicationProperties.emplace( + std::make_pair(static_cast(val.first), val.second)); } } } + } + { + annotations footerVal; + if (!message_get_footer(message, &footerVal) && footerVal) { - annotations footerVal; - if (!message_get_footer(message, &footerVal) && footerVal) - { - UniqueAmqpValueHandle footerAnnotations(footerVal); - footerVal = nullptr; - auto footerMap = AmqpValue{footerAnnotations.get()}.AsMap(); - rv.Footer = footerMap; - } + UniqueAmqpValueHandle footerAnnotations(footerVal); + footerVal = nullptr; + auto footerMap = AmqpValue{footerAnnotations.get()}.AsMap(); + rv.Footer = footerMap; } - { - MESSAGE_BODY_TYPE bodyType; + } + { + MESSAGE_BODY_TYPE bodyType; - if (!message_get_body_type(message, &bodyType)) + if (!message_get_body_type(message, &bodyType)) + { + switch (bodyType) { - switch (bodyType) - { - case MESSAGE_BODY_TYPE_NONE: - rv.BodyType = MessageBodyType::None; - break; - case MESSAGE_BODY_TYPE_DATA: { - size_t dataCount; - if (!message_get_body_amqp_data_count(message, &dataCount)) + case MESSAGE_BODY_TYPE_NONE: + rv.BodyType = MessageBodyType::None; + break; + case MESSAGE_BODY_TYPE_DATA: { + size_t dataCount; + if (!message_get_body_amqp_data_count(message, &dataCount)) + { + for (auto i = 0ul; i < dataCount; i += 1) { - for (auto i = 0ul; i < dataCount; i += 1) + BINARY_DATA binaryValue; + if (!message_get_body_amqp_data_in_place(message, i, &binaryValue)) { - BINARY_DATA binaryValue; - if (!message_get_body_amqp_data_in_place(message, i, &binaryValue)) - { - rv.m_binaryDataBody.push_back(AmqpBinaryData(std::vector( - binaryValue.bytes, binaryValue.bytes + binaryValue.length))); - } + rv.m_binaryDataBody.push_back(AmqpBinaryData(std::vector( + binaryValue.bytes, binaryValue.bytes + binaryValue.length))); } } - rv.BodyType = MessageBodyType::Data; } - break; - case MESSAGE_BODY_TYPE_SEQUENCE: { + rv.BodyType = MessageBodyType::Data; + } + break; + case MESSAGE_BODY_TYPE_SEQUENCE: { - size_t sequenceCount; - if (!message_get_body_amqp_sequence_count(message, &sequenceCount)) + size_t sequenceCount; + if (!message_get_body_amqp_sequence_count(message, &sequenceCount)) + { + for (auto i = 0ul; i < sequenceCount; i += 1) { - for (auto i = 0ul; i < sequenceCount; i += 1) + AMQP_VALUE sequence; + if (!message_get_body_amqp_sequence_in_place(message, i, &sequence)) { - AMQP_VALUE sequence; - if (!message_get_body_amqp_sequence_in_place(message, i, &sequence)) - { - rv.m_amqpSequenceBody.push_back(sequence); - } + rv.m_amqpSequenceBody.push_back(sequence); } } - rv.BodyType = MessageBodyType::Sequence; } - break; - case MESSAGE_BODY_TYPE_VALUE: { - AMQP_VALUE bodyValue; - if (!message_get_body_amqp_value_in_place(message, &bodyValue)) - { - rv.m_amqpValueBody = bodyValue; - } - rv.BodyType = MessageBodyType::Value; + rv.BodyType = MessageBodyType::Sequence; + } + break; + case MESSAGE_BODY_TYPE_VALUE: { + AMQP_VALUE bodyValue; + if (!message_get_body_amqp_value_in_place(message, &bodyValue)) + { + rv.m_amqpValueBody = bodyValue; } - break; - case MESSAGE_BODY_TYPE_INVALID: - default: - throw std::runtime_error("Unknown body type."); + rv.BodyType = MessageBodyType::Value; } + break; + case MESSAGE_BODY_TYPE_INVALID: + default: + throw std::runtime_error("Unknown body type."); } } - return rv; } + return rv; + } + + UniqueMessageHandle _internal::AmqpMessageFactory::ToUamqp(AmqpMessage const& message) + { + UniqueMessageHandle rv(message_create()); - UniqueMessageHandle _internal::AmqpMessageFactory::ToUamqp(AmqpMessage const& message) + // AMQP 1.0 specifies a message format of 0. + if (message_set_message_format(rv.get(), AmqpMessageFormatValue)) { - UniqueMessageHandle rv(message_create()); + throw std::runtime_error("Could not set destination message format."); + } - // AMQP 1.0 specifies a message format of 0. - if (message_set_message_format(rv.get(), AmqpMessageFormatValue)) - { - throw std::runtime_error("Could not set destination message format."); - } + if (message_set_header( + rv.get(), _internal::MessageHeaderFactory::ToUamqp(message.Header).get())) + { + throw std::runtime_error("Could not set message header."); + } + if (message_set_properties( + rv.get(), _internal::MessagePropertiesFactory::ToUamqp(message.Properties).get())) + { + throw std::runtime_error("Could not set message properties."); + } - if (message_set_header( - rv.get(), _internal::MessageHeaderFactory::ToUamqp(message.Header).get())) + if (!message.DeliveryAnnotations.empty()) + { + if (message_set_delivery_annotations( + rv.get(), static_cast(message.DeliveryAnnotations).get())) { - throw std::runtime_error("Could not set message header."); + throw std::runtime_error("Could not set delivery annotations."); } - if (message_set_properties( - rv.get(), _internal::MessagePropertiesFactory::ToUamqp(message.Properties).get())) + } + if (!message.MessageAnnotations.empty()) + { + if (message_set_message_annotations( + rv.get(), static_cast(message.MessageAnnotations).get())) { - throw std::runtime_error("Could not set message properties."); + throw std::runtime_error("Could not set message annotations."); } - - if (!message.DeliveryAnnotations.empty()) + } + if (!message.ApplicationProperties.empty()) + { + AmqpMap appProperties; + for (auto const& val : message.ApplicationProperties) { - if (message_set_delivery_annotations( - rv.get(), static_cast(message.DeliveryAnnotations).get())) + if ((val.second.GetType() == AmqpValueType::List) + || (val.second.GetType() == AmqpValueType::Map) + || (val.second.GetType() == AmqpValueType::Composite) + || (val.second.GetType() == AmqpValueType::Described)) { - throw std::runtime_error("Could not set delivery annotations."); + throw std::runtime_error( + "Message Application Property values must be simple value types"); } + appProperties.emplace(val); } - if (!message.MessageAnnotations.empty()) + if (message_set_application_properties( + rv.get(), static_cast(appProperties).get())) { - if (message_set_message_annotations( - rv.get(), static_cast(message.MessageAnnotations).get())) - { - throw std::runtime_error("Could not set message annotations."); - } + throw std::runtime_error("Could not set application properties."); } - if (!message.ApplicationProperties.empty()) + } + if (!message.Footer.empty()) + { + if (message_set_footer(rv.get(), static_cast(message.Footer).get())) { - AmqpMap appProperties; - for (auto const& val : message.ApplicationProperties) + throw std::runtime_error("Could not set message annotations."); + } + } + switch (message.BodyType) + { + case MessageBodyType::None: + break; + case MessageBodyType::Data: + for (auto const& binaryVal : message.m_binaryDataBody) { - if ((val.second.GetType() == AmqpValueType::List) - || (val.second.GetType() == AmqpValueType::Map) - || (val.second.GetType() == AmqpValueType::Composite) - || (val.second.GetType() == AmqpValueType::Described)) + BINARY_DATA valueData; + valueData.bytes = binaryVal.data(); + valueData.length = static_cast(binaryVal.size()); + if (message_add_body_amqp_data(rv.get(), valueData)) { - throw std::runtime_error( - "Message Application Property values must be simple value types"); + throw std::runtime_error("Could not set message body AMQP sequence value."); } - appProperties.emplace(val); } - if (message_set_application_properties( - rv.get(), static_cast(appProperties).get())) + break; + case MessageBodyType::Sequence: + for (auto const& sequenceVal : message.m_amqpSequenceBody) { - throw std::runtime_error("Could not set application properties."); + if (message_add_body_amqp_sequence(rv.get(), AmqpValue(sequenceVal))) + { + throw std::runtime_error("Could not set message body AMQP sequence value."); + } } - } - if (!message.Footer.empty()) - { - if (message_set_footer(rv.get(), static_cast(message.Footer).get())) + break; + case MessageBodyType::Value: + if (message_set_body_amqp_value(rv.get(), message.m_amqpValueBody)) { - throw std::runtime_error("Could not set message annotations."); + throw std::runtime_error("Could not set message body AMQP value."); } - } - switch (message.BodyType) - { - case MessageBodyType::None: - break; - case MessageBodyType::Data: - for (auto const& binaryVal : message.m_binaryDataBody) - { - BINARY_DATA valueData; - valueData.bytes = binaryVal.data(); - valueData.length = static_cast(binaryVal.size()); - if (message_add_body_amqp_data(rv.get(), valueData)) - { - throw std::runtime_error("Could not set message body AMQP sequence value."); - } - } - break; - case MessageBodyType::Sequence: - for (auto const& sequenceVal : message.m_amqpSequenceBody) - { - if (message_add_body_amqp_sequence(rv.get(), AmqpValue(sequenceVal))) - { - throw std::runtime_error("Could not set message body AMQP sequence value."); - } - } - break; - case MessageBodyType::Value: - if (message_set_body_amqp_value(rv.get(), message.m_amqpValueBody)) - { - throw std::runtime_error("Could not set message body AMQP value."); - } - break; - case MessageBodyType::Invalid: // LCOV_EXCL_LINE - default: // LCOV_EXCL_LINE - throw std::runtime_error("Unknown message body type."); // LCOV_EXCL_LINE - } - return rv; + break; + case MessageBodyType::Invalid: // LCOV_EXCL_LINE + default: // LCOV_EXCL_LINE + throw std::runtime_error("Unknown message body type."); // LCOV_EXCL_LINE } + return rv; + } - std::vector AmqpMessage::GetBodyAsAmqpList() const + std::vector AmqpMessage::GetBodyAsAmqpList() const + { + if (BodyType != MessageBodyType::Sequence) { - if (BodyType != MessageBodyType::Sequence) - { - throw std::runtime_error("Invalid body type, should be MessageBodyType::Sequence."); - } - return m_amqpSequenceBody; + throw std::runtime_error("Invalid body type, should be MessageBodyType::Sequence."); } + return m_amqpSequenceBody; + } + + void AmqpMessage::SetBody(AmqpBinaryData const& value) + { + BodyType = MessageBodyType::Data; + m_binaryDataBody.push_back(value); + } + void AmqpMessage::SetBody(std::vector const& value) + { + BodyType = MessageBodyType::Data; + m_binaryDataBody = value; + } + void AmqpMessage::SetBody(AmqpValue const& value) + { + BodyType = MessageBodyType::Value; + m_amqpValueBody = value; + } + void AmqpMessage::SetBody(std::vector const& value) + { + BodyType = MessageBodyType::Sequence; + m_amqpSequenceBody = value; + } + void AmqpMessage::SetBody(AmqpList const& value) + { + BodyType = MessageBodyType::Sequence; + m_amqpSequenceBody.push_back(value); + } - void AmqpMessage::SetBody(AmqpBinaryData const& value) + AmqpValue AmqpMessage::GetBodyAsAmqpValue() const + { + if (BodyType != MessageBodyType::Value) { - BodyType = MessageBodyType::Data; - m_binaryDataBody.push_back(value); + throw std::runtime_error("Invalid body type, should be MessageBodyType::Value."); } - void AmqpMessage::SetBody(std::vector const& value) + return m_amqpValueBody; + } + std::vector AmqpMessage::GetBodyAsBinary() const + { + if (BodyType != MessageBodyType::Data) { - BodyType = MessageBodyType::Data; - m_binaryDataBody = value; + throw std::runtime_error("Invalid body type, should be MessageBodyType::Value."); } - void AmqpMessage::SetBody(AmqpValue const& value) + return m_binaryDataBody; + } + + bool AmqpMessage::operator==(AmqpMessage const& that) const noexcept + { + return (Header == that.Header) && (DeliveryAnnotations == that.DeliveryAnnotations) + && (MessageAnnotations == that.MessageAnnotations) && (Properties == that.Properties) + && (ApplicationProperties == that.ApplicationProperties) && (Footer == that.Footer) + && (BodyType == that.BodyType) && (m_amqpValueBody == that.m_amqpValueBody) + && (m_amqpSequenceBody == that.m_amqpSequenceBody) + && (m_binaryDataBody == that.m_binaryDataBody); + } + + size_t AmqpMessage::GetSerializedSize(AmqpMessage const& message) + { + size_t serializedSize{}; + + serializedSize += MessageHeader::GetSerializedSize(message.Header); + serializedSize += AmqpValue::GetSerializedSize(message.MessageAnnotations); + serializedSize += MessageProperties::GetSerializedSize(message.Properties); + + // ApplicationProperties is a map of string to value, we need to convert it to an AmqpMap. { - BodyType = MessageBodyType::Value; - m_amqpValueBody = value; + AmqpMap appProperties; + for (auto const& val : message.ApplicationProperties) + { + if ((val.second.GetType() == AmqpValueType::List) + || (val.second.GetType() == AmqpValueType::Map) + || (val.second.GetType() == AmqpValueType::Composite) + || (val.second.GetType() == AmqpValueType::Described)) + { + throw std::runtime_error( + "Message Application Property values must be simple value types"); + } + appProperties.emplace(val); + } + serializedSize += AmqpValue::GetSerializedSize(appProperties); } - void AmqpMessage::SetBody(std::vector const& value) + + switch (message.BodyType) { - BodyType = MessageBodyType::Sequence; - m_amqpSequenceBody = value; + default: + case MessageBodyType::Invalid: + throw std::runtime_error("Invalid message body type."); + + case MessageBodyType::Value: + serializedSize += AmqpValue::GetSerializedSize(message.m_amqpValueBody); + break; + case MessageBodyType::Data: + for (auto const& val : message.m_binaryDataBody) + { + serializedSize += AmqpValue::GetSerializedSize(val); + } + break; + case MessageBodyType::Sequence: + for (auto const& val : message.m_amqpSequenceBody) + { + serializedSize += AmqpValue::GetSerializedSize(val); + } + break; } - void AmqpMessage::SetBody(AmqpList const& value) + return serializedSize; + } + + std::vector AmqpMessage::Serialize(AmqpMessage const& message) + { + std::vector rv; + + // Append the message Header to the serialized message. + if (message.Header.ShouldSerialize()) { - BodyType = MessageBodyType::Sequence; - m_amqpSequenceBody.push_back(value); + auto serializedHeader = MessageHeader::Serialize(message.Header); + rv.insert(rv.end(), serializedHeader.begin(), serializedHeader.end()); } - - AmqpValue AmqpMessage::GetBodyAsAmqpValue() const + if (!message.DeliveryAnnotations.empty()) { - if (BodyType != MessageBodyType::Value) - { - throw std::runtime_error("Invalid body type, should be MessageBodyType::Value."); - } - return m_amqpValueBody; + AmqpValue deliveryAnnotations{ + amqpvalue_create_delivery_annotations(AmqpValue{message.DeliveryAnnotations})}; + auto serializedDeliveryAnnotations = AmqpValue::Serialize(deliveryAnnotations); + rv.insert( + rv.end(), serializedDeliveryAnnotations.begin(), serializedDeliveryAnnotations.end()); } - std::vector AmqpMessage::GetBodyAsBinary() const + if (!message.MessageAnnotations.empty()) { - if (BodyType != MessageBodyType::Data) - { - throw std::runtime_error("Invalid body type, should be MessageBodyType::Value."); - } - return m_binaryDataBody; + AmqpValue messageAnnotations{ + amqpvalue_create_message_annotations(AmqpValue{message.MessageAnnotations})}; + auto serializedAnnotations = AmqpValue::Serialize(messageAnnotations); + rv.insert(rv.end(), serializedAnnotations.begin(), serializedAnnotations.end()); } - bool AmqpMessage::operator==(AmqpMessage const& that) const noexcept + if (message.Properties.ShouldSerialize()) { - return (Header == that.Header) && (DeliveryAnnotations == that.DeliveryAnnotations) - && (MessageAnnotations == that.MessageAnnotations) && (Properties == that.Properties) - && (ApplicationProperties == that.ApplicationProperties) && (Footer == that.Footer) - && (BodyType == that.BodyType) && (m_amqpValueBody == that.m_amqpValueBody) - && (m_amqpSequenceBody == that.m_amqpSequenceBody) - && (m_binaryDataBody == that.m_binaryDataBody); + auto serializedMessageProperties = MessageProperties::Serialize(message.Properties); + rv.insert(rv.end(), serializedMessageProperties.begin(), serializedMessageProperties.end()); } - size_t AmqpMessage::GetSerializedSize(AmqpMessage const& message) + if (!message.ApplicationProperties.empty()) { - size_t serializedSize{}; - - serializedSize += MessageHeader::GetSerializedSize(message.Header); - serializedSize += AmqpValue::GetSerializedSize(message.MessageAnnotations); - serializedSize += MessageProperties::GetSerializedSize(message.Properties); - - // ApplicationProperties is a map of string to value, we need to convert it to an AmqpMap. + AmqpMap appProperties; + for (auto const& val : message.ApplicationProperties) { - AmqpMap appProperties; - for (auto const& val : message.ApplicationProperties) + if ((val.second.GetType() == AmqpValueType::List) + || (val.second.GetType() == AmqpValueType::Map) + || (val.second.GetType() == AmqpValueType::Composite) + || (val.second.GetType() == AmqpValueType::Described)) { - if ((val.second.GetType() == AmqpValueType::List) - || (val.second.GetType() == AmqpValueType::Map) - || (val.second.GetType() == AmqpValueType::Composite) - || (val.second.GetType() == AmqpValueType::Described)) - { - throw std::runtime_error( - "Message Application Property values must be simple value types"); - } - appProperties.emplace(val); + throw std::runtime_error( + "Message Application Property values must be simple value types"); } - serializedSize += AmqpValue::GetSerializedSize(appProperties); + appProperties.emplace(val); } + AmqpValue propertiesValue{amqpvalue_create_application_properties(AmqpValue{appProperties})}; + auto serializedApplicationProperties = AmqpValue::Serialize(propertiesValue); + rv.insert( + rv.end(), serializedApplicationProperties.begin(), serializedApplicationProperties.end()); + } - switch (message.BodyType) - { - default: - case MessageBodyType::Invalid: - throw std::runtime_error("Invalid message body type."); - - case MessageBodyType::Value: - serializedSize += AmqpValue::GetSerializedSize(message.m_amqpValueBody); - break; - case MessageBodyType::Data: - for (auto const& val : message.m_binaryDataBody) - { - serializedSize += AmqpValue::GetSerializedSize(val); - } - break; - case MessageBodyType::Sequence: - for (auto const& val : message.m_amqpSequenceBody) - { - serializedSize += AmqpValue::GetSerializedSize(val); - } - break; + switch (message.BodyType) + { + default: + case MessageBodyType::Invalid: + throw std::runtime_error("Invalid message body type."); + + case MessageBodyType::Value: { + // The message body element is an AMQP Described type, create one and serialize the + // described body. + AmqpDescribed describedBody( + static_cast(AmqpDescriptors::DataAmqpValue), message.m_amqpValueBody); + auto serializedBodyValue = AmqpValue::Serialize(describedBody); + rv.insert(rv.end(), serializedBodyValue.begin(), serializedBodyValue.end()); + } + break; + case MessageBodyType::Data: + for (auto const& val : message.m_binaryDataBody) + { + AmqpDescribed describedBody(static_cast(AmqpDescriptors::DataBinary), val); + auto serializedBodyValue = AmqpValue::Serialize(describedBody); + rv.insert(rv.end(), serializedBodyValue.begin(), serializedBodyValue.end()); + } + break; + case MessageBodyType::Sequence: { + for (auto const& val : message.m_amqpSequenceBody) + { + AmqpDescribed describedBody( + static_cast(AmqpDescriptors::DataAmqpSequence), val); + auto serializedBodyValue = AmqpValue::Serialize(describedBody); + rv.insert(rv.end(), serializedBodyValue.begin(), serializedBodyValue.end()); + } } - return serializedSize; } - - std::vector AmqpMessage::Serialize(AmqpMessage const& message) + if (!message.Footer.empty()) { - std::vector rv; + AmqpValue footer{amqpvalue_create_footer(AmqpValue{message.Footer})}; + auto serializedFooter = AmqpValue::Serialize(footer); + rv.insert(rv.end(), serializedFooter.begin(), serializedFooter.end()); + } - // Append the message Header to the serialized message. - if (message.Header.ShouldSerialize()) - { - auto serializedHeader = MessageHeader::Serialize(message.Header); - rv.insert(rv.end(), serializedHeader.begin(), serializedHeader.end()); - } - if (!message.DeliveryAnnotations.empty()) - { - AmqpValue deliveryAnnotations{ - amqpvalue_create_delivery_annotations(AmqpValue{message.DeliveryAnnotations})}; - auto serializedDeliveryAnnotations = AmqpValue::Serialize(deliveryAnnotations); - rv.insert( - rv.end(), serializedDeliveryAnnotations.begin(), serializedDeliveryAnnotations.end()); - } - if (!message.MessageAnnotations.empty()) - { - AmqpValue messageAnnotations{ - amqpvalue_create_message_annotations(AmqpValue{message.MessageAnnotations})}; - auto serializedAnnotations = AmqpValue::Serialize(messageAnnotations); - rv.insert(rv.end(), serializedAnnotations.begin(), serializedAnnotations.end()); - } + return rv; + } - if (message.Properties.ShouldSerialize()) + namespace { + class AmqpMessageDeserializer { + public: + AmqpMessageDeserializer() + : m_decoder{amqpvalue_decoder_create(OnAmqpMessageFieldDecodedFn, this)} { - auto serializedMessageProperties = MessageProperties::Serialize(message.Properties); - rv.insert(rv.end(), serializedMessageProperties.begin(), serializedMessageProperties.end()); } - if (!message.ApplicationProperties.empty()) + AmqpMessage operator()(std::uint8_t const* data, size_t size) const { - AmqpMap appProperties; - for (auto const& val : message.ApplicationProperties) + if (amqpvalue_decode_bytes(m_decoder.get(), data, size)) { - if ((val.second.GetType() == AmqpValueType::List) - || (val.second.GetType() == AmqpValueType::Map) - || (val.second.GetType() == AmqpValueType::Composite) - || (val.second.GetType() == AmqpValueType::Described)) - { - throw std::runtime_error( - "Message Application Property values must be simple value types"); - } - appProperties.emplace(val); + throw std::runtime_error("Could not decode object"); } - AmqpValue propertiesValue{ - amqpvalue_create_application_properties(AmqpValue{appProperties})}; - auto serializedApplicationProperties = AmqpValue::Serialize(propertiesValue); - rv.insert( - rv.end(), - serializedApplicationProperties.begin(), - serializedApplicationProperties.end()); + return m_decodedValue; } - switch (message.BodyType) - { - default: - case MessageBodyType::Invalid: - throw std::runtime_error("Invalid message body type."); + private: + UniqueAmqpDecoderHandle m_decoder; + AmqpMessage m_decodedValue; + // The message fields, in their expected order. + std::set m_expectedMessageFields{ + AmqpDescriptors::Header, + AmqpDescriptors::DeliveryAnnotations, + AmqpDescriptors::MessageAnnotations, + AmqpDescriptors::Properties, + AmqpDescriptors::ApplicationProperties, + AmqpDescriptors::DataAmqpSequence, + AmqpDescriptors::DataAmqpValue, + AmqpDescriptors::DataBinary, + AmqpDescriptors::Footer}; - case MessageBodyType::Value: { - // The message body element is an AMQP Described type, create one and serialize the - // described body. - AmqpDescribed describedBody( - static_cast(AmqpDescriptors::DataAmqpValue), message.m_amqpValueBody); - auto serializedBodyValue = AmqpValue::Serialize(describedBody); - rv.insert(rv.end(), serializedBodyValue.begin(), serializedBodyValue.end()); - } - break; - case MessageBodyType::Data: - for (auto const& val : message.m_binaryDataBody) - { - AmqpDescribed describedBody( - static_cast(AmqpDescriptors::DataBinary), val); - auto serializedBodyValue = AmqpValue::Serialize(describedBody); - rv.insert(rv.end(), serializedBodyValue.begin(), serializedBodyValue.end()); - } - break; - case MessageBodyType::Sequence: { - for (auto const& val : message.m_amqpSequenceBody) - { - AmqpDescribed describedBody( - static_cast(AmqpDescriptors::DataAmqpSequence), val); - auto serializedBodyValue = AmqpValue::Serialize(describedBody); - rv.insert(rv.end(), serializedBodyValue.begin(), serializedBodyValue.end()); - } - } - } - if (!message.Footer.empty()) + // Invoked on each descriptor encountered while decrypting the message. + static void OnAmqpMessageFieldDecodedFn(void* context, AMQP_VALUE value) { - AmqpValue footer{amqpvalue_create_footer(AmqpValue{message.Footer})}; - auto serializedFooter = AmqpValue::Serialize(footer); - rv.insert(rv.end(), serializedFooter.begin(), serializedFooter.end()); - } + auto deserializer = static_cast(context); - return rv; - } + deserializer->OnAmqpMessageFieldDecoded(value); + } - namespace { - class AmqpMessageDeserializer { - public: - AmqpMessageDeserializer() - : m_decoder{amqpvalue_decoder_create(OnAmqpMessageFieldDecodedFn, this)} + // Invoked when a message field + void OnAmqpMessageFieldDecoded(AmqpValue value) + { + if (value.GetType() != AmqpValueType::Described) { + throw std::runtime_error("Decoded message field whose type is NOT described."); } - - AmqpMessage operator()(std::uint8_t const* data, size_t size) const + AmqpDescribed describedType(value.AsDescribed()); + if (describedType.GetDescriptor().GetType() != AmqpValueType::Ulong) { - if (amqpvalue_decode_bytes(m_decoder.get(), data, size)) - { - throw std::runtime_error("Could not decode object"); - } - return m_decodedValue; + throw std::runtime_error("Decoded message field MUST be a LONG type."); } - private: - UniqueAmqpDecoderHandle m_decoder; - AmqpMessage m_decodedValue; - // The message fields, in their expected order. - std::set m_expectedMessageFields{ - AmqpDescriptors::Header, - AmqpDescriptors::DeliveryAnnotations, - AmqpDescriptors::MessageAnnotations, - AmqpDescriptors::Properties, - AmqpDescriptors::ApplicationProperties, - AmqpDescriptors::DataAmqpSequence, - AmqpDescriptors::DataAmqpValue, - AmqpDescriptors::DataBinary, - AmqpDescriptors::Footer}; - - // Invoked on each descriptor encountered while decrypting the message. - static void OnAmqpMessageFieldDecodedFn(void* context, AMQP_VALUE value) + AmqpDescriptors fieldDescriptor( + static_cast(static_cast(describedType.GetDescriptor()))); + if (m_expectedMessageFields.find(fieldDescriptor) == m_expectedMessageFields.end()) { - auto deserializer = static_cast(context); - - deserializer->OnAmqpMessageFieldDecoded(value); + throw std::runtime_error("Found message field is not in the set of expected fields."); } - - // Invoked when a message field - void OnAmqpMessageFieldDecoded(AmqpValue value) + else { - if (value.GetType() != AmqpValueType::Described) - { - throw std::runtime_error("Decoded message field whose type is NOT described."); - } - AmqpDescribed describedType(value.AsDescribed()); - if (describedType.GetDescriptor().GetType() != AmqpValueType::Ulong) - { - throw std::runtime_error("Decoded message field MUST be a LONG type."); - } - - AmqpDescriptors fieldDescriptor( - static_cast(static_cast(describedType.GetDescriptor()))); - if (m_expectedMessageFields.find(fieldDescriptor) == m_expectedMessageFields.end()) - { - throw std::runtime_error("Found message field is not in the set of expected fields."); - } - else - { - // Once we've seen a field, we can remove that field from the set of expected fields, - // and also the fields which must come before it. - // - // The two exceptions are the DataBinary and DataAmqpSequence fields, which can have - // more than one instance. - switch (fieldDescriptor) - { - case AmqpDescriptors::Header: - m_expectedMessageFields.erase(AmqpDescriptors::Header); - break; - case AmqpDescriptors::DeliveryAnnotations: - // Once we've seen a DeliveryAnnotations, we no longer expect to see a Header or - // another DeliveryAnnotations. - m_expectedMessageFields.erase(AmqpDescriptors::Header); - m_expectedMessageFields.erase(AmqpDescriptors::DeliveryAnnotations); - break; - case AmqpDescriptors::MessageAnnotations: - // Once we've seen a MessageAnnotations, we no longer expect to see a Header, - // DeliveryAnnotations, or a MessageAnnotations. - m_expectedMessageFields.erase(AmqpDescriptors::Header); - m_expectedMessageFields.erase(AmqpDescriptors::DeliveryAnnotations); - m_expectedMessageFields.erase(AmqpDescriptors::MessageAnnotations); - break; - case AmqpDescriptors::Properties: - m_expectedMessageFields.erase(AmqpDescriptors::Header); - m_expectedMessageFields.erase(AmqpDescriptors::DeliveryAnnotations); - m_expectedMessageFields.erase(AmqpDescriptors::MessageAnnotations); - m_expectedMessageFields.erase(AmqpDescriptors::Properties); - break; - case AmqpDescriptors::ApplicationProperties: - m_expectedMessageFields.erase(AmqpDescriptors::Header); - m_expectedMessageFields.erase(AmqpDescriptors::DeliveryAnnotations); - m_expectedMessageFields.erase(AmqpDescriptors::MessageAnnotations); - m_expectedMessageFields.erase(AmqpDescriptors::Properties); - m_expectedMessageFields.erase(AmqpDescriptors::ApplicationProperties); - break; - case AmqpDescriptors::DataAmqpSequence: - m_expectedMessageFields.erase(AmqpDescriptors::Header); - m_expectedMessageFields.erase(AmqpDescriptors::DeliveryAnnotations); - m_expectedMessageFields.erase(AmqpDescriptors::MessageAnnotations); - m_expectedMessageFields.erase(AmqpDescriptors::Properties); - m_expectedMessageFields.erase(AmqpDescriptors::ApplicationProperties); - // When we see an DataAmqpSequence, we no longer expect to see any other data type. - m_expectedMessageFields.erase(AmqpDescriptors::DataAmqpValue); - m_expectedMessageFields.erase(AmqpDescriptors::DataBinary); - break; - case AmqpDescriptors::DataAmqpValue: - m_expectedMessageFields.erase(AmqpDescriptors::Header); - m_expectedMessageFields.erase(AmqpDescriptors::DeliveryAnnotations); - m_expectedMessageFields.erase(AmqpDescriptors::MessageAnnotations); - m_expectedMessageFields.erase(AmqpDescriptors::Properties); - m_expectedMessageFields.erase(AmqpDescriptors::ApplicationProperties); - // When we see an DataAmqpValue, we no longer expect to see any other data type. - m_expectedMessageFields.erase(AmqpDescriptors::DataAmqpValue); - m_expectedMessageFields.erase(AmqpDescriptors::DataAmqpSequence); - m_expectedMessageFields.erase(AmqpDescriptors::DataBinary); - break; - case AmqpDescriptors::DataBinary: - m_expectedMessageFields.erase(AmqpDescriptors::Header); - m_expectedMessageFields.erase(AmqpDescriptors::DeliveryAnnotations); - m_expectedMessageFields.erase(AmqpDescriptors::MessageAnnotations); - m_expectedMessageFields.erase(AmqpDescriptors::Properties); - m_expectedMessageFields.erase(AmqpDescriptors::ApplicationProperties); - // When we see an DataBinary, we no longer expect to see any other data type. - m_expectedMessageFields.erase(AmqpDescriptors::DataAmqpValue); - m_expectedMessageFields.erase(AmqpDescriptors::DataAmqpSequence); - break; - case AmqpDescriptors::Footer: - m_expectedMessageFields.erase(AmqpDescriptors::Header); - m_expectedMessageFields.erase(AmqpDescriptors::DeliveryAnnotations); - m_expectedMessageFields.erase(AmqpDescriptors::MessageAnnotations); - m_expectedMessageFields.erase(AmqpDescriptors::Properties); - m_expectedMessageFields.erase(AmqpDescriptors::ApplicationProperties); - // When we see an DataBinary, we no longer expect to see any other data type. - m_expectedMessageFields.erase(AmqpDescriptors::DataBinary); - m_expectedMessageFields.erase(AmqpDescriptors::DataAmqpValue); - m_expectedMessageFields.erase(AmqpDescriptors::DataAmqpSequence); - m_expectedMessageFields.erase(AmqpDescriptors::Footer); - break; - default: - throw std::runtime_error("Unknown message descriptor."); - } - } - + // Once we've seen a field, we can remove that field from the set of expected fields, + // and also the fields which must come before it. + // + // The two exceptions are the DataBinary and DataAmqpSequence fields, which can have + // more than one instance. switch (fieldDescriptor) { - case AmqpDescriptors::Header: { - UniqueMessageHeaderHandle messageHeader; - HEADER_HANDLE h; - if (amqpvalue_get_header(value, &h)) - { - throw std::runtime_error("Could not convert field to header."); - } - messageHeader.reset(h); - h = nullptr; - m_decodedValue.Header = _internal::MessageHeaderFactory::FromUamqp(messageHeader); + case AmqpDescriptors::Header: + m_expectedMessageFields.erase(AmqpDescriptors::Header); break; - } case AmqpDescriptors::DeliveryAnnotations: - m_decodedValue.DeliveryAnnotations = describedType.GetValue().AsMap(); + // Once we've seen a DeliveryAnnotations, we no longer expect to see a Header or + // another DeliveryAnnotations. + m_expectedMessageFields.erase(AmqpDescriptors::Header); + m_expectedMessageFields.erase(AmqpDescriptors::DeliveryAnnotations); break; case AmqpDescriptors::MessageAnnotations: - m_decodedValue.MessageAnnotations = describedType.GetValue().AsMap(); - break; - case AmqpDescriptors::Properties: { - UniquePropertiesHandle properties; - PROPERTIES_HANDLE h; - if (amqpvalue_get_properties(value, &h)) - { - throw std::runtime_error("Could not convert field to header."); - } - properties.reset(h); - h = nullptr; - m_decodedValue.Properties - = _internal::MessagePropertiesFactory::FromUamqp(properties); + // Once we've seen a MessageAnnotations, we no longer expect to see a Header, + // DeliveryAnnotations, or a MessageAnnotations. + m_expectedMessageFields.erase(AmqpDescriptors::Header); + m_expectedMessageFields.erase(AmqpDescriptors::DeliveryAnnotations); + m_expectedMessageFields.erase(AmqpDescriptors::MessageAnnotations); break; - } - case AmqpDescriptors::ApplicationProperties: { - auto propertyMap = describedType.GetValue().AsMap(); - for (auto const& val : propertyMap) - { - if (val.first.GetType() != AmqpValueType::String) - { - throw std::runtime_error("Key of applications properties must be a string."); - } - if ((val.second.GetType() == AmqpValueType::List) - || (val.second.GetType() == AmqpValueType::Map) - || (val.second.GetType() == AmqpValueType::Composite) - || (val.second.GetType() == AmqpValueType::Described)) - { - throw std::runtime_error( - "Message Application Property values must be simple value types"); - } - m_decodedValue.ApplicationProperties.emplace( - static_cast(val.first), val.second); - } + case AmqpDescriptors::Properties: + m_expectedMessageFields.erase(AmqpDescriptors::Header); + m_expectedMessageFields.erase(AmqpDescriptors::DeliveryAnnotations); + m_expectedMessageFields.erase(AmqpDescriptors::MessageAnnotations); + m_expectedMessageFields.erase(AmqpDescriptors::Properties); break; - } - case AmqpDescriptors::DataAmqpValue: - m_decodedValue.SetBody(describedType.GetValue()); + case AmqpDescriptors::ApplicationProperties: + m_expectedMessageFields.erase(AmqpDescriptors::Header); + m_expectedMessageFields.erase(AmqpDescriptors::DeliveryAnnotations); + m_expectedMessageFields.erase(AmqpDescriptors::MessageAnnotations); + m_expectedMessageFields.erase(AmqpDescriptors::Properties); + m_expectedMessageFields.erase(AmqpDescriptors::ApplicationProperties); break; case AmqpDescriptors::DataAmqpSequence: - m_decodedValue.SetBody(describedType.GetValue().AsList()); + m_expectedMessageFields.erase(AmqpDescriptors::Header); + m_expectedMessageFields.erase(AmqpDescriptors::DeliveryAnnotations); + m_expectedMessageFields.erase(AmqpDescriptors::MessageAnnotations); + m_expectedMessageFields.erase(AmqpDescriptors::Properties); + m_expectedMessageFields.erase(AmqpDescriptors::ApplicationProperties); + // When we see an DataAmqpSequence, we no longer expect to see any other data type. + m_expectedMessageFields.erase(AmqpDescriptors::DataAmqpValue); + m_expectedMessageFields.erase(AmqpDescriptors::DataBinary); + break; + case AmqpDescriptors::DataAmqpValue: + m_expectedMessageFields.erase(AmqpDescriptors::Header); + m_expectedMessageFields.erase(AmqpDescriptors::DeliveryAnnotations); + m_expectedMessageFields.erase(AmqpDescriptors::MessageAnnotations); + m_expectedMessageFields.erase(AmqpDescriptors::Properties); + m_expectedMessageFields.erase(AmqpDescriptors::ApplicationProperties); + // When we see an DataAmqpValue, we no longer expect to see any other data type. + m_expectedMessageFields.erase(AmqpDescriptors::DataAmqpValue); + m_expectedMessageFields.erase(AmqpDescriptors::DataAmqpSequence); + m_expectedMessageFields.erase(AmqpDescriptors::DataBinary); break; case AmqpDescriptors::DataBinary: - // Each call to SetBody will append the binary value to the vector of binary bodies. - m_decodedValue.SetBody(describedType.GetValue().AsBinary()); + m_expectedMessageFields.erase(AmqpDescriptors::Header); + m_expectedMessageFields.erase(AmqpDescriptors::DeliveryAnnotations); + m_expectedMessageFields.erase(AmqpDescriptors::MessageAnnotations); + m_expectedMessageFields.erase(AmqpDescriptors::Properties); + m_expectedMessageFields.erase(AmqpDescriptors::ApplicationProperties); + // When we see an DataBinary, we no longer expect to see any other data type. + m_expectedMessageFields.erase(AmqpDescriptors::DataAmqpValue); + m_expectedMessageFields.erase(AmqpDescriptors::DataAmqpSequence); break; case AmqpDescriptors::Footer: - m_decodedValue.Footer = describedType.GetValue().AsMap(); + m_expectedMessageFields.erase(AmqpDescriptors::Header); + m_expectedMessageFields.erase(AmqpDescriptors::DeliveryAnnotations); + m_expectedMessageFields.erase(AmqpDescriptors::MessageAnnotations); + m_expectedMessageFields.erase(AmqpDescriptors::Properties); + m_expectedMessageFields.erase(AmqpDescriptors::ApplicationProperties); + // When we see an DataBinary, we no longer expect to see any other data type. + m_expectedMessageFields.erase(AmqpDescriptors::DataBinary); + m_expectedMessageFields.erase(AmqpDescriptors::DataAmqpValue); + m_expectedMessageFields.erase(AmqpDescriptors::DataAmqpSequence); + m_expectedMessageFields.erase(AmqpDescriptors::Footer); break; - default: // LCOV_EXCL_LINE - throw std::runtime_error("Unknown message descriptor."); // LCOV_EXCL_LINE + default: + throw std::runtime_error("Unknown message descriptor."); } } - }; - } // namespace - - AmqpMessage AmqpMessage::Deserialize(std::uint8_t const* buffer, size_t size) - { - return AmqpMessageDeserializer{}(buffer, size); - } - std::ostream& operator<<(std::ostream& os, AmqpMessage const& message) - { - os << "Message: " << std::endl; - os << "Header " << message.Header << std::endl; - os << "Properties: " << message.Properties; - os << "Body: [" << std::endl; - switch (message.BodyType) - { - case MessageBodyType::Invalid: // LCOV_EXCL_LINE - os << "Invalid"; // LCOV_EXCL_LINE - break; // LCOV_EXCL_LINE - case MessageBodyType::None: - os << "None"; - break; - case MessageBodyType::Data: { - os << "AMQP Data: ["; - auto bodyBinary = message.GetBodyAsBinary(); - uint8_t i = 0; - for (auto const& val : bodyBinary) - { - os << "Data: " << val << std::endl; - if (i < bodyBinary.size() - 1) + switch (fieldDescriptor) + { + case AmqpDescriptors::Header: { + UniqueMessageHeaderHandle messageHeader; + HEADER_HANDLE h; + if (amqpvalue_get_header(value, &h)) { - os << ", "; + throw std::runtime_error("Could not convert field to header."); } - i += 1; + messageHeader.reset(h); + h = nullptr; + m_decodedValue.Header = _internal::MessageHeaderFactory::FromUamqp(messageHeader); + break; } - os << "]"; - } - break; - case MessageBodyType::Sequence: { - os << "AMQP Sequence: ["; - auto bodySequence = message.GetBodyAsAmqpList(); - uint8_t i = 0; - for (auto const& val : bodySequence) - { - os << "Sequence: " << val << std::endl; - if (i < bodySequence.size() - 1) + case AmqpDescriptors::DeliveryAnnotations: + m_decodedValue.DeliveryAnnotations = describedType.GetValue().AsMap(); + break; + case AmqpDescriptors::MessageAnnotations: + m_decodedValue.MessageAnnotations = describedType.GetValue().AsMap(); + break; + case AmqpDescriptors::Properties: { + UniquePropertiesHandle properties; + PROPERTIES_HANDLE h; + if (amqpvalue_get_properties(value, &h)) + { + throw std::runtime_error("Could not convert field to header."); + } + properties.reset(h); + h = nullptr; + m_decodedValue.Properties = _internal::MessagePropertiesFactory::FromUamqp(properties); + break; + } + case AmqpDescriptors::ApplicationProperties: { + auto propertyMap = describedType.GetValue().AsMap(); + for (auto const& val : propertyMap) { - os << ", "; + if (val.first.GetType() != AmqpValueType::String) + { + throw std::runtime_error("Key of applications properties must be a string."); + } + if ((val.second.GetType() == AmqpValueType::List) + || (val.second.GetType() == AmqpValueType::Map) + || (val.second.GetType() == AmqpValueType::Composite) + || (val.second.GetType() == AmqpValueType::Described)) + { + throw std::runtime_error( + "Message Application Property values must be simple value types"); + } + m_decodedValue.ApplicationProperties.emplace( + static_cast(val.first), val.second); } - i += 1; + break; } - os << "]"; + case AmqpDescriptors::DataAmqpValue: + m_decodedValue.SetBody(describedType.GetValue()); + break; + case AmqpDescriptors::DataAmqpSequence: + m_decodedValue.SetBody(describedType.GetValue().AsList()); + break; + case AmqpDescriptors::DataBinary: + // Each call to SetBody will append the binary value to the vector of binary bodies. + m_decodedValue.SetBody(describedType.GetValue().AsBinary()); + break; + case AmqpDescriptors::Footer: + m_decodedValue.Footer = describedType.GetValue().AsMap(); + break; + default: // LCOV_EXCL_LINE + throw std::runtime_error("Unknown message descriptor."); // LCOV_EXCL_LINE } - break; - case MessageBodyType::Value: - os << "AmqpValue: " << message.GetBodyAsAmqpValue(); - break; } - os << "]"; + }; + } // namespace - { - if (!message.ApplicationProperties.empty()) + AmqpMessage AmqpMessage::Deserialize(std::uint8_t const* buffer, size_t size) + { + return AmqpMessageDeserializer{}(buffer, size); + } + + std::ostream& operator<<(std::ostream& os, AmqpMessage const& message) + { + os << "Message: " << std::endl; + os << "Header " << message.Header << std::endl; + os << "Properties: " << message.Properties; + os << "Body: [" << std::endl; + switch (message.BodyType) + { + case MessageBodyType::Invalid: // LCOV_EXCL_LINE + os << "Invalid"; // LCOV_EXCL_LINE + break; // LCOV_EXCL_LINE + case MessageBodyType::None: + os << "None"; + break; + case MessageBodyType::Data: { + os << "AMQP Data: ["; + auto bodyBinary = message.GetBodyAsBinary(); + uint8_t i = 0; + for (auto const& val : bodyBinary) { - os << std::endl << "Application Properties: "; - for (auto const& val : message.ApplicationProperties) + os << "Data: " << val << std::endl; + if (i < bodyBinary.size() - 1) { - os << "{" << val.first << ", " << val.second << "}"; + os << ", "; } + i += 1; } - } - if (!message.DeliveryAnnotations.empty()) - { - os << std::endl << "Delivery Annotations: "; - for (auto const& val : message.DeliveryAnnotations) + os << "]"; + } + break; + case MessageBodyType::Sequence: { + os << "AMQP Sequence: ["; + auto bodySequence = message.GetBodyAsAmqpList(); + uint8_t i = 0; + for (auto const& val : bodySequence) { - os << "{" << val.first << ", " << val.second << "}"; + os << "Sequence: " << val << std::endl; + if (i < bodySequence.size() - 1) + { + os << ", "; + } + i += 1; } + os << "]"; } - if (!message.MessageAnnotations.empty()) + break; + case MessageBodyType::Value: + os << "AmqpValue: " << message.GetBodyAsAmqpValue(); + break; + } + os << "]"; + + { + if (!message.ApplicationProperties.empty()) { - os << std::endl << "Message Annotations: "; - for (auto const& val : message.MessageAnnotations) + os << std::endl << "Application Properties: "; + for (auto const& val : message.ApplicationProperties) { os << "{" << val.first << ", " << val.second << "}"; } } - if (!message.Footer.empty()) + } + if (!message.DeliveryAnnotations.empty()) + { + os << std::endl << "Delivery Annotations: "; + for (auto const& val : message.DeliveryAnnotations) { - os << "Footer: "; - for (auto const& val : message.Footer) - { - os << "{" << val.first << ", " << val.second << "}"; - } + os << "{" << val.first << ", " << val.second << "}"; + } + } + if (!message.MessageAnnotations.empty()) + { + os << std::endl << "Message Annotations: "; + for (auto const& val : message.MessageAnnotations) + { + os << "{" << val.first << ", " << val.second << "}"; } - return os; } + if (!message.Footer.empty()) + { + os << "Footer: "; + for (auto const& val : message.Footer) + { + os << "{" << val.first << ", " << val.second << "}"; + } + } + return os; + } }}}} // namespace Azure::Core::Amqp::Models diff --git a/sdk/core/azure-core-amqp/test/ut/amqp_value_tests.cpp b/sdk/core/azure-core-amqp/test/ut/amqp_value_tests.cpp index 7b40594363..53564cc8a5 100644 --- a/sdk/core/azure-core-amqp/test/ut/amqp_value_tests.cpp +++ b/sdk/core/azure-core-amqp/test/ut/amqp_value_tests.cpp @@ -136,7 +136,7 @@ TEST_F(TestValues, SimpleCreate) EXPECT_TRUE(AmqpValue() < value); } - { + { AmqpValue value1{29}; AmqpValue value2(std::move(value1)); AmqpValue value3(value2); From 643fa844fa0a9fac55ebd9afef2ffd4fb59d7e87 Mon Sep 17 00:00:00 2001 From: Larry Osterman Date: Thu, 27 Apr 2023 15:12:32 -0700 Subject: [PATCH 14/17] improved code coverage --- .../azure-core-amqp/src/amqp/management.cpp | 381 +++++++++--------- .../azure-core-amqp/src/models/amqp_value.cpp | 2 +- .../src/network/sasl_transport.cpp | 2 +- .../test/ut/amqp_value_tests.cpp | 44 +- .../test/ut/management_tests.cpp | 12 +- 5 files changed, 243 insertions(+), 198 deletions(-) diff --git a/sdk/core/azure-core-amqp/src/amqp/management.cpp b/sdk/core/azure-core-amqp/src/amqp/management.cpp index a240432d64..1624697cbc 100644 --- a/sdk/core/azure-core-amqp/src/amqp/management.cpp +++ b/sdk/core/azure-core-amqp/src/amqp/management.cpp @@ -22,217 +22,214 @@ void Azure::Core::_internal::UniqueHandleHelper::F amqp_management_destroy(value); } -namespace Azure { namespace Core { namespace Amqp { - namespace _internal { - - /** - * @brief Create a new Management object instance. - * - * @param session - the session on which to create the instance. - * @param managementNodeName - the name of the message source and target. - * @param options - additional options for the Management object. - * @param managementEvents - events associated with the management object. - */ - Management::Management( - Session const& session, - std::string const& managementNodeName, - ManagementOptions const& options, - ManagementEvents* managementEvents) - : m_impl{std::make_shared<_detail::ManagementImpl>( - session.GetImpl(), - managementNodeName, - options, - managementEvents)} +namespace Azure { namespace Core { namespace Amqp { namespace _internal { + + /** + * @brief Create a new Management object instance. + * + * @param session - the session on which to create the instance. + * @param managementNodeName - the name of the message source and target. + * @param options - additional options for the Management object. + * @param managementEvents - events associated with the management object. + */ + Management::Management( + Session const& session, + std::string const& managementNodeName, + ManagementOptions const& options, + ManagementEvents* managementEvents) + : m_impl{std::make_shared<_detail::ManagementImpl>( + session.GetImpl(), + managementNodeName, + options, + managementEvents)} + { + } + + Management::operator bool() const { return static_cast(m_impl); } + + /** + * @brief Open the management instance. + * + * @returns A tuple consisting of the status code for the open and the description of the + * status. + */ + ManagementOpenResult Management::Open(Azure::Core::Context const& context) + { + return m_impl->Open(context); + } + + /** + * @brief Close the management instance. + */ + void Management::Close() { m_impl->Close(); } + + std::tuple<_internal::ManagementOperationResult, std::uint32_t, std::string, Models::AmqpMessage> + Management::ExecuteOperation( + std::string const& operationToPerform, + std::string const& typeOfOperation, + std::string const& locales, + Azure::Core::Amqp::Models::AmqpMessage const& messageToSend, + Azure::Core::Context context) + { + return m_impl->ExecuteOperation( + operationToPerform, typeOfOperation, locales, messageToSend, context); + } +}}}} // namespace Azure::Core::Amqp::_internal + +namespace Azure { namespace Core { namespace Amqp { namespace _detail { + ManagementImpl::ManagementImpl( + std::shared_ptr session, + std::string const& managementNodeName, + Azure::Core::Amqp::_internal::ManagementOptions const& options, + Azure::Core::Amqp::_internal::ManagementEvents* managementEvents) + : m_management{amqp_management_create(*session, managementNodeName.c_str())}, + m_options{options}, m_session{session}, m_eventHandler{managementEvents} + { + if (options.EnableTrace) { + amqp_management_set_trace(m_management.get(), options.EnableTrace); } - - Management::operator bool() const { return static_cast(m_impl); } - - /** - * @brief Open the management instance. - * - * @returns A tuple consisting of the status code for the open and the description of the - * status. - */ - ManagementOpenResult Management::Open(Azure::Core::Context const& context) + if (!options.ExpectedStatusCodeKeyName.empty()) { - return m_impl->Open(context); + amqp_management_set_override_status_code_key_name( + m_management.get(), options.ExpectedStatusCodeKeyName.c_str()); } - - /** - * @brief Close the management instance. - */ - void Management::Close() { m_impl->Close(); } - - std:: - tuple<_internal::ManagementOperationResult, std::uint32_t, std::string, Models::AmqpMessage> - Management::ExecuteOperation( - std::string const& operationToPerform, - std::string const& typeOfOperation, - std::string const& locales, - Azure::Core::Amqp::Models::AmqpMessage const& messageToSend, - Azure::Core::Context context) + if (!options.ExpectedStatusDescriptionKeyName.empty()) { - return m_impl->ExecuteOperation( - operationToPerform, typeOfOperation, locales, messageToSend, context); + amqp_management_set_override_status_description_key_name( + m_management.get(), options.ExpectedStatusDescriptionKeyName.c_str()); } - } // namespace _internal - namespace _detail { - ManagementImpl::ManagementImpl( - std::shared_ptr session, - std::string const& managementNodeName, - Azure::Core::Amqp::_internal::ManagementOptions const& options, - Azure::Core::Amqp::_internal::ManagementEvents* managementEvents) - : m_management{amqp_management_create(*session, managementNodeName.c_str())}, - m_options{options}, m_session{session}, m_eventHandler{managementEvents} + } + ManagementImpl::~ManagementImpl() noexcept { m_eventHandler = nullptr; } + + ManagementImpl::operator bool() const { return static_cast(m_management); } + + _internal::ManagementOpenResult ManagementImpl::Open(Azure::Core::Context const& context) + { + if (amqp_management_open_async( + m_management.get(), + ManagementImpl::OnOpenCompleteFn, + this, + ManagementImpl::OnManagementErrorFn, + this)) { - if (options.EnableTrace) - { - amqp_management_set_trace(m_management.get(), options.EnableTrace); - } - if (!options.ExpectedStatusCodeKeyName.empty()) - { - amqp_management_set_override_status_code_key_name( - m_management.get(), options.ExpectedStatusCodeKeyName.c_str()); - } - if (!options.ExpectedStatusDescriptionKeyName.empty()) - { - amqp_management_set_override_status_description_key_name( - m_management.get(), options.ExpectedStatusDescriptionKeyName.c_str()); - } + throw std::runtime_error("Could not open management object."); } - ManagementImpl::~ManagementImpl() noexcept { m_eventHandler = nullptr; } - - ManagementImpl::operator bool() const { return static_cast(m_management); } - - _internal::ManagementOpenResult ManagementImpl::Open(Azure::Core::Context const& context) + auto result + = m_openCompleteQueue.WaitForPolledResult(context, *m_session->GetConnectionToPoll()); + if (result) { - if (amqp_management_open_async( - m_management.get(), - ManagementImpl::OnOpenCompleteFn, - this, - ManagementImpl::OnManagementErrorFn, - this)) + switch (std::get<0>(*result)) { - throw std::runtime_error("Could not open management object."); + case AMQP_MANAGEMENT_OPEN_OK: + return _internal::ManagementOpenResult::Ok; + case AMQP_MANAGEMENT_OPEN_ERROR: // LCOV_EXCL_LINE + return _internal::ManagementOpenResult::Error; // LCOV_EXCL_LINE + case AMQP_MANAGEMENT_OPEN_CANCELLED: // LCOV_EXCL_LINE + return _internal::ManagementOpenResult::Cancelled; // LCOV_EXCL_LINE + default: // LCOV_EXCL_LINE + throw std::runtime_error("Unknown management open result."); // LCOV_EXCL_LINE } - auto result - = m_openCompleteQueue.WaitForPolledResult(context, *m_session->GetConnectionToPoll()); - if (result) - { - switch (std::get<0>(*result)) - { - case AMQP_MANAGEMENT_OPEN_OK: - return _internal::ManagementOpenResult::Ok; - case AMQP_MANAGEMENT_OPEN_ERROR: - return _internal::ManagementOpenResult::Error; - case AMQP_MANAGEMENT_OPEN_CANCELLED: - return _internal::ManagementOpenResult::Cancelled; - default: - throw std::runtime_error("Unknown management open result."); - } - } - throw Azure::Core::OperationCancelledException("Management Open operation was cancelled."); } - - std:: - tuple<_internal::ManagementOperationResult, std::uint32_t, std::string, Models::AmqpMessage> - ManagementImpl::ExecuteOperation( - std::string const& operationToPerform, - std::string const& typeOfOperation, - std::string const& locales, - Azure::Core::Amqp::Models::AmqpMessage const& messageToSend, - Azure::Core::Context context) + throw Azure::Core::OperationCancelledException("Management Open operation was cancelled."); + } + + std::tuple<_internal::ManagementOperationResult, std::uint32_t, std::string, Models::AmqpMessage> + ManagementImpl::ExecuteOperation( + std::string const& operationToPerform, + std::string const& typeOfOperation, + std::string const& locales, + Azure::Core::Amqp::Models::AmqpMessage const& messageToSend, + Azure::Core::Context context) + { + if (!amqp_management_execute_operation_async( + m_management.get(), + operationToPerform.c_str(), + typeOfOperation.c_str(), + locales.c_str(), + Models::_internal::AmqpMessageFactory::ToUamqp(messageToSend).get(), + ManagementImpl::OnExecuteOperationCompleteFn, + this)) { - if (!amqp_management_execute_operation_async( - m_management.get(), - operationToPerform.c_str(), - typeOfOperation.c_str(), - locales.c_str(), - Models::_internal::AmqpMessageFactory::ToUamqp(messageToSend).get(), - ManagementImpl::OnExecuteOperationCompleteFn, - this)) - { - throw std::runtime_error("Could not execute operation."); - } - - auto result = m_messageQueue.WaitForPolledResult(context, *m_session->GetConnectionToPoll()); - if (result) - { - return std::move(*result); - } - else - { - throw Azure::Core::OperationCancelledException("Management operation cancelled."); - } + throw std::runtime_error("Could not execute operation."); // LCOV_EXCL_LINE } - void ManagementImpl::Close() { amqp_management_close(m_management.get()); } - - void ManagementImpl::OnOpenCompleteFn(void* context, AMQP_MANAGEMENT_OPEN_RESULT openResult) + auto result = m_messageQueue.WaitForPolledResult(context, *m_session->GetConnectionToPoll()); + if (result) { - ManagementImpl* management = static_cast(context); - management->m_openCompleteQueue.CompleteOperation(openResult); + return std::move(*result); } - - void ManagementImpl::OnManagementErrorFn(void* context) + else { - Azure::Core::Diagnostics::_internal::Log::Write( - Azure::Core::Diagnostics::Logger::Level::Error, "Error processing management operation."); - ManagementImpl* management = static_cast(context); - if (management->m_eventHandler) - { - management->m_eventHandler->OnError(); - } - management->m_messageQueue.CompleteOperation( - _internal::ManagementOperationResult::Error, - 0, - "Error processing management operation.", - Models::AmqpMessage()); + throw Azure::Core::OperationCancelledException("Management operation cancelled."); } - - void ManagementImpl::OnExecuteOperationCompleteFn( - void* context, - AMQP_MANAGEMENT_EXECUTE_OPERATION_RESULT result, - std::uint32_t statusCode, - const char* statusDescription, - MESSAGE_HANDLE message) + } + + void ManagementImpl::Close() { amqp_management_close(m_management.get()); } + + void ManagementImpl::OnOpenCompleteFn(void* context, AMQP_MANAGEMENT_OPEN_RESULT openResult) + { + ManagementImpl* management = static_cast(context); + management->m_openCompleteQueue.CompleteOperation(openResult); + } + + void ManagementImpl::OnManagementErrorFn(void* context) + { + Azure::Core::Diagnostics::_internal::Log::Write( + Azure::Core::Diagnostics::Logger::Level::Error, "Error processing management operation."); + ManagementImpl* management = static_cast(context); + if (management->m_eventHandler) { - ManagementImpl* management = static_cast(context); - switch (result) - { - case AMQP_MANAGEMENT_EXECUTE_OPERATION_OK: - management->m_messageQueue.CompleteOperation( - _internal::ManagementOperationResult::Ok, - statusCode, - (statusDescription ? statusDescription : std::string()), - Models::_internal::AmqpMessageFactory::FromUamqp(message)); - break; - case AMQP_MANAGEMENT_EXECUTE_OPERATION_ERROR: - management->m_messageQueue.CompleteOperation( - _internal::ManagementOperationResult::Error, - statusCode, - (statusDescription ? statusDescription : std::string()), - Models::_internal::AmqpMessageFactory::FromUamqp(message)); - break; - case AMQP_MANAGEMENT_EXECUTE_OPERATION_FAILED_BAD_STATUS: - management->m_messageQueue.CompleteOperation( - _internal::ManagementOperationResult::FailedBadStatus, - statusCode, - (statusDescription ? statusDescription : std::string()), - Models::_internal::AmqpMessageFactory::FromUamqp(message)); - break; - case AMQP_MANAGEMENT_EXECUTE_OPERATION_INSTANCE_CLOSED: - management->m_messageQueue.CompleteOperation( - _internal::ManagementOperationResult::InstanceClosed, - statusCode, - (statusDescription ? statusDescription : std::string()), - Models::_internal::AmqpMessageFactory::FromUamqp(message)); - break; - default: - throw std::runtime_error("Unknown management status."); - } + management->m_eventHandler->OnError(); + } + management->m_messageQueue.CompleteOperation( + _internal::ManagementOperationResult::Error, + 0, + "Error processing management operation.", + Models::AmqpMessage()); + } + + void ManagementImpl::OnExecuteOperationCompleteFn( + void* context, + AMQP_MANAGEMENT_EXECUTE_OPERATION_RESULT result, + std::uint32_t statusCode, + const char* statusDescription, + MESSAGE_HANDLE message) + { + ManagementImpl* management = static_cast(context); + switch (result) + { + case AMQP_MANAGEMENT_EXECUTE_OPERATION_OK: + management->m_messageQueue.CompleteOperation( + _internal::ManagementOperationResult::Ok, + statusCode, + (statusDescription ? statusDescription : std::string()), + Models::_internal::AmqpMessageFactory::FromUamqp(message)); + break; + case AMQP_MANAGEMENT_EXECUTE_OPERATION_ERROR: + management->m_messageQueue.CompleteOperation( + _internal::ManagementOperationResult::Error, + statusCode, + (statusDescription ? statusDescription : std::string()), + Models::_internal::AmqpMessageFactory::FromUamqp(message)); + break; + case AMQP_MANAGEMENT_EXECUTE_OPERATION_FAILED_BAD_STATUS: + management->m_messageQueue.CompleteOperation( + _internal::ManagementOperationResult::FailedBadStatus, + statusCode, + (statusDescription ? statusDescription : std::string()), + Models::_internal::AmqpMessageFactory::FromUamqp(message)); + break; + case AMQP_MANAGEMENT_EXECUTE_OPERATION_INSTANCE_CLOSED: + management->m_messageQueue.CompleteOperation( + _internal::ManagementOperationResult::InstanceClosed, + statusCode, + (statusDescription ? statusDescription : std::string()), + Models::_internal::AmqpMessageFactory::FromUamqp(message)); + break; + default: // LCOV_EXCL_LINE + throw std::runtime_error("Unknown management status."); // LCOV_EXCL_LINE } + } - } // namespace _detail -}}} // namespace Azure::Core::Amqp +}}}} // namespace Azure::Core::Amqp::_detail diff --git a/sdk/core/azure-core-amqp/src/models/amqp_value.cpp b/sdk/core/azure-core-amqp/src/models/amqp_value.cpp index 2a1611e025..0846c9485c 100644 --- a/sdk/core/azure-core-amqp/src/models/amqp_value.cpp +++ b/sdk/core/azure-core-amqp/src/models/amqp_value.cpp @@ -427,7 +427,7 @@ namespace Azure { namespace Core { namespace Amqp { namespace Models { { if (amqpvalue_encode(value, OnAmqpValueEncoded, this)) { - throw std::runtime_error("Could not decode object"); + throw std::runtime_error("Could not encode object"); } return m_encodedValue; diff --git a/sdk/core/azure-core-amqp/src/network/sasl_transport.cpp b/sdk/core/azure-core-amqp/src/network/sasl_transport.cpp index 1d7c381f28..d023052db0 100644 --- a/sdk/core/azure-core-amqp/src/network/sasl_transport.cpp +++ b/sdk/core/azure-core-amqp/src/network/sasl_transport.cpp @@ -77,4 +77,4 @@ Azure::Core::Amqp::Network::_internal::SaslTransport::SaslTransport( saslConfig.sasl_mechanism = saslMechanism; SetInstance(xio_create(saslclientio_get_interface_description(), &saslConfig)); } -// LCOV_EXCL_END +// LCOV_EXCL_STOP diff --git a/sdk/core/azure-core-amqp/test/ut/amqp_value_tests.cpp b/sdk/core/azure-core-amqp/test/ut/amqp_value_tests.cpp index 53564cc8a5..e40b73f26b 100644 --- a/sdk/core/azure-core-amqp/test/ut/amqp_value_tests.cpp +++ b/sdk/core/azure-core-amqp/test/ut/amqp_value_tests.cpp @@ -33,25 +33,41 @@ TEST_F(TestValues, SimpleCreate) EXPECT_FALSE(value); EXPECT_TRUE(AmqpValue() < value); } + { + EXPECT_LT(AmqpValue(false), AmqpValue(true)); + } { AmqpValue value{}; EXPECT_TRUE(value.IsNull()); } + { + AmqpValue value{static_cast(-17)}; + EXPECT_EQ(AmqpValueType::Byte, value.GetType()); + EXPECT_EQ(-17, static_cast(value)); + EXPECT_TRUE(AmqpValue() < value); + EXPECT_LT(AmqpValue{static_cast(-18)}, value); + EXPECT_ANY_THROW(static_cast(value)); + } + { AmqpValue value{static_cast(255)}; EXPECT_EQ(AmqpValueType::Ubyte, value.GetType()); EXPECT_EQ(255, static_cast(value)); EXPECT_TRUE(AmqpValue() < value); + EXPECT_ANY_THROW(static_cast(value)); + EXPECT_LT(AmqpValue{static_cast(254)}, value); } { - AmqpValue value{'A'}; + AmqpValue value{'D'}; EXPECT_EQ(AmqpValueType::Byte, value.GetType()); - EXPECT_EQ(static_cast(65), static_cast(value)); + EXPECT_EQ(static_cast(68), static_cast(value)); EXPECT_TRUE(AmqpValue() < value); char ch{value}; - EXPECT_EQ('A', ch); + EXPECT_EQ('D', ch); + EXPECT_ANY_THROW(static_cast(value)); + EXPECT_LT(AmqpValue('B'), value); } { @@ -59,12 +75,16 @@ TEST_F(TestValues, SimpleCreate) EXPECT_EQ(AmqpValueType::Ushort, value.GetType()); EXPECT_EQ(65535, static_cast(value)); EXPECT_TRUE(AmqpValue() < value); + EXPECT_ANY_THROW(static_cast(value)); + EXPECT_LT(AmqpValue{static_cast(65534)}, value); } { AmqpValue value{static_cast(32767)}; EXPECT_EQ(AmqpValueType::Short, value.GetType()); EXPECT_EQ(32767, static_cast(value)); EXPECT_TRUE(AmqpValue() < value); + EXPECT_ANY_THROW(static_cast(value)); + EXPECT_LT(AmqpValue{static_cast(32766)}, value); } { @@ -72,12 +92,16 @@ TEST_F(TestValues, SimpleCreate) EXPECT_EQ(AmqpValueType::Int, value.GetType()); EXPECT_EQ(32, static_cast(value)); EXPECT_TRUE(AmqpValue() < value); + EXPECT_ANY_THROW(static_cast(value)); + EXPECT_LT(AmqpValue(31), value); } { AmqpValue value(32u); EXPECT_EQ(AmqpValueType::Uint, value.GetType()); EXPECT_EQ(32u, static_cast(value)); EXPECT_TRUE(AmqpValue() < value); + EXPECT_ANY_THROW(static_cast(value)); + EXPECT_LT(AmqpValue(31u), value); } { @@ -85,12 +109,16 @@ TEST_F(TestValues, SimpleCreate) EXPECT_EQ(AmqpValueType::Long, value.GetType()); EXPECT_EQ(32ll, static_cast(value)); EXPECT_TRUE(AmqpValue() < value); + EXPECT_ANY_THROW(static_cast(value)); + EXPECT_LT(AmqpValue(static_cast(31ll)), value); } { AmqpValue value(static_cast(39ull)); EXPECT_EQ(AmqpValueType::Ulong, value.GetType()); EXPECT_EQ(39ull, static_cast(value)); EXPECT_TRUE(AmqpValue() < value); + EXPECT_ANY_THROW(static_cast(value)); + EXPECT_LT(AmqpValue(static_cast(38ull)), value); } { @@ -98,12 +126,16 @@ TEST_F(TestValues, SimpleCreate) EXPECT_EQ(AmqpValueType::Float, value.GetType()); EXPECT_EQ(39.0f, static_cast(value)); EXPECT_TRUE(AmqpValue() < value); + EXPECT_ANY_THROW(static_cast(value)); + EXPECT_LT(AmqpValue(38.0f), value); } { AmqpValue value(39.0); EXPECT_EQ(AmqpValueType::Double, value.GetType()); EXPECT_EQ(39.0, static_cast(value)); EXPECT_TRUE(AmqpValue() < value); + EXPECT_ANY_THROW(static_cast(value)); + EXPECT_LT(AmqpValue(38.0), value); } { @@ -119,6 +151,8 @@ TEST_F(TestValues, SimpleCreate) EXPECT_EQ(AmqpValueType::String, value.GetType()); EXPECT_EQ(std::string("Fred"), fredP); EXPECT_TRUE(AmqpValue() < value); + EXPECT_ANY_THROW(static_cast(value)); + EXPECT_LT(AmqpValue("ABC"), value); } { AmqpValue value("Fred"); @@ -126,6 +160,7 @@ TEST_F(TestValues, SimpleCreate) EXPECT_EQ(AmqpValueType::String, value.GetType()); EXPECT_EQ(std::string("Fred"), fredP); EXPECT_TRUE(AmqpValue() < value); + EXPECT_ANY_THROW(static_cast(value)); } { @@ -134,6 +169,7 @@ TEST_F(TestValues, SimpleCreate) EXPECT_EQ(AmqpValueType::Uuid, value.GetType()); EXPECT_EQ(uuid.ToString(), static_cast(value).ToString()); EXPECT_TRUE(AmqpValue() < value); + EXPECT_ANY_THROW(static_cast(value)); } { @@ -264,6 +300,7 @@ TEST_F(TestValues, TestChar) { AmqpValue value{U'\U0001f34c'}; EXPECT_EQ(U'\U0001f34c', static_cast(value)); + EXPECT_EQ(AmqpValueType::Char, value.GetType()); EXPECT_FALSE(static_cast(value) < U'\U0001f34c'); } { @@ -308,6 +345,7 @@ TEST_F(TestValues, TestCompositeValue) { { AmqpComposite value("My Composite Type", {1, 2, 5.5, "ABC", 5}); + EXPECT_EQ(AmqpValueType::Composite, AmqpValue(value).GetType()); EXPECT_EQ(5, value.size()); } diff --git a/sdk/core/azure-core-amqp/test/ut/management_tests.cpp b/sdk/core/azure-core-amqp/test/ut/management_tests.cpp index f471c538f7..34e15da339 100644 --- a/sdk/core/azure-core-amqp/test/ut/management_tests.cpp +++ b/sdk/core/azure-core-amqp/test/ut/management_tests.cpp @@ -22,12 +22,14 @@ TEST_F(TestManagement, BasicTests) { { Management management; + EXPECT_FALSE(management); } { Connection connection("amqps://localhost:5151", {}); Session session(connection); Management management(session, "Test", {}); + EXPECT_TRUE(management); } } @@ -253,12 +255,19 @@ TEST_F(TestManagement, ManagementRequestResponse) { ManagementReceiver mockServer; + struct ManagementEventsHandler : public ManagementEvents + { + void OnError() override { Error = true; } + bool Error{false}; + }; + ManagementEventsHandler managementEvents; + Connection connection("amqp://localhost:" + std::to_string(mockServer.GetPort()), {}); Session session(connection); ManagementOptions options; options.EnableTrace = true; - Management management(session, "Test", options); + Management management(session, "Test", options, &managementEvents); // Set the response status code to something other than an int - that will cause the response to // be rejected by the management client. @@ -277,6 +286,7 @@ TEST_F(TestManagement, ManagementRequestResponse) EXPECT_EQ(std::get<0>(response), ManagementOperationResult::Error); EXPECT_EQ(std::get<1>(response), 0); EXPECT_EQ(std::get<2>(response), "Error processing management operation."); + EXPECT_TRUE(managementEvents.Error); management.Close(); mockServer.StopListening(); From 02141cfaf7e4dc632fbf6e054a8740c34aa883fb Mon Sep 17 00:00:00 2001 From: Larry Osterman Date: Thu, 27 Apr 2023 16:04:21 -0700 Subject: [PATCH 15/17] clang fix 8 --- sdk/core/azure-core-amqp/test/ut/amqp_value_tests.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sdk/core/azure-core-amqp/test/ut/amqp_value_tests.cpp b/sdk/core/azure-core-amqp/test/ut/amqp_value_tests.cpp index e40b73f26b..f3bb6d8d2c 100644 --- a/sdk/core/azure-core-amqp/test/ut/amqp_value_tests.cpp +++ b/sdk/core/azure-core-amqp/test/ut/amqp_value_tests.cpp @@ -55,7 +55,7 @@ TEST_F(TestValues, SimpleCreate) EXPECT_EQ(AmqpValueType::Ubyte, value.GetType()); EXPECT_EQ(255, static_cast(value)); EXPECT_TRUE(AmqpValue() < value); - EXPECT_ANY_THROW(static_cast(value)); + EXPECT_ANY_THROW((void)static_cast(value)); EXPECT_LT(AmqpValue{static_cast(254)}, value); } @@ -66,7 +66,7 @@ TEST_F(TestValues, SimpleCreate) EXPECT_TRUE(AmqpValue() < value); char ch{value}; EXPECT_EQ('D', ch); - EXPECT_ANY_THROW(static_cast(value)); + EXPECT_ANY_THROW((void)static_cast(value)); EXPECT_LT(AmqpValue('B'), value); } @@ -75,7 +75,7 @@ TEST_F(TestValues, SimpleCreate) EXPECT_EQ(AmqpValueType::Ushort, value.GetType()); EXPECT_EQ(65535, static_cast(value)); EXPECT_TRUE(AmqpValue() < value); - EXPECT_ANY_THROW(static_cast(value)); + EXPECT_ANY_THROW((void)static_cast(value)); EXPECT_LT(AmqpValue{static_cast(65534)}, value); } { @@ -100,7 +100,7 @@ TEST_F(TestValues, SimpleCreate) EXPECT_EQ(AmqpValueType::Uint, value.GetType()); EXPECT_EQ(32u, static_cast(value)); EXPECT_TRUE(AmqpValue() < value); - EXPECT_ANY_THROW(static_cast(value)); + EXPECT_ANY_THROW((void)static_cast(value)); EXPECT_LT(AmqpValue(31u), value); } From 5286cd9560a2c73116031ee80675141a13e37e9f Mon Sep 17 00:00:00 2001 From: Larry Osterman Date: Thu, 27 Apr 2023 16:21:16 -0700 Subject: [PATCH 16/17] clang fix 9 --- .../test/ut/amqp_value_tests.cpp | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/sdk/core/azure-core-amqp/test/ut/amqp_value_tests.cpp b/sdk/core/azure-core-amqp/test/ut/amqp_value_tests.cpp index f3bb6d8d2c..0290f4e9de 100644 --- a/sdk/core/azure-core-amqp/test/ut/amqp_value_tests.cpp +++ b/sdk/core/azure-core-amqp/test/ut/amqp_value_tests.cpp @@ -47,7 +47,7 @@ TEST_F(TestValues, SimpleCreate) EXPECT_EQ(-17, static_cast(value)); EXPECT_TRUE(AmqpValue() < value); EXPECT_LT(AmqpValue{static_cast(-18)}, value); - EXPECT_ANY_THROW(static_cast(value)); + EXPECT_ANY_THROW((void)static_cast(value)); } { @@ -83,7 +83,7 @@ TEST_F(TestValues, SimpleCreate) EXPECT_EQ(AmqpValueType::Short, value.GetType()); EXPECT_EQ(32767, static_cast(value)); EXPECT_TRUE(AmqpValue() < value); - EXPECT_ANY_THROW(static_cast(value)); + EXPECT_ANY_THROW((void)static_cast(value)); EXPECT_LT(AmqpValue{static_cast(32766)}, value); } @@ -92,7 +92,7 @@ TEST_F(TestValues, SimpleCreate) EXPECT_EQ(AmqpValueType::Int, value.GetType()); EXPECT_EQ(32, static_cast(value)); EXPECT_TRUE(AmqpValue() < value); - EXPECT_ANY_THROW(static_cast(value)); + EXPECT_ANY_THROW((void)static_cast(value)); EXPECT_LT(AmqpValue(31), value); } { @@ -109,7 +109,7 @@ TEST_F(TestValues, SimpleCreate) EXPECT_EQ(AmqpValueType::Long, value.GetType()); EXPECT_EQ(32ll, static_cast(value)); EXPECT_TRUE(AmqpValue() < value); - EXPECT_ANY_THROW(static_cast(value)); + EXPECT_ANY_THROW((void)static_cast(value)); EXPECT_LT(AmqpValue(static_cast(31ll)), value); } { @@ -117,7 +117,7 @@ TEST_F(TestValues, SimpleCreate) EXPECT_EQ(AmqpValueType::Ulong, value.GetType()); EXPECT_EQ(39ull, static_cast(value)); EXPECT_TRUE(AmqpValue() < value); - EXPECT_ANY_THROW(static_cast(value)); + EXPECT_ANY_THROW((void)static_cast(value)); EXPECT_LT(AmqpValue(static_cast(38ull)), value); } @@ -126,7 +126,7 @@ TEST_F(TestValues, SimpleCreate) EXPECT_EQ(AmqpValueType::Float, value.GetType()); EXPECT_EQ(39.0f, static_cast(value)); EXPECT_TRUE(AmqpValue() < value); - EXPECT_ANY_THROW(static_cast(value)); + EXPECT_ANY_THROW((void)static_cast(value)); EXPECT_LT(AmqpValue(38.0f), value); } { @@ -134,7 +134,7 @@ TEST_F(TestValues, SimpleCreate) EXPECT_EQ(AmqpValueType::Double, value.GetType()); EXPECT_EQ(39.0, static_cast(value)); EXPECT_TRUE(AmqpValue() < value); - EXPECT_ANY_THROW(static_cast(value)); + EXPECT_ANY_THROW((void)static_cast(value)); EXPECT_LT(AmqpValue(38.0), value); } @@ -151,7 +151,7 @@ TEST_F(TestValues, SimpleCreate) EXPECT_EQ(AmqpValueType::String, value.GetType()); EXPECT_EQ(std::string("Fred"), fredP); EXPECT_TRUE(AmqpValue() < value); - EXPECT_ANY_THROW(static_cast(value)); + EXPECT_ANY_THROW((void)static_cast(value)); EXPECT_LT(AmqpValue("ABC"), value); } { @@ -160,7 +160,7 @@ TEST_F(TestValues, SimpleCreate) EXPECT_EQ(AmqpValueType::String, value.GetType()); EXPECT_EQ(std::string("Fred"), fredP); EXPECT_TRUE(AmqpValue() < value); - EXPECT_ANY_THROW(static_cast(value)); + EXPECT_ANY_THROW((bool)static_cast(value)); } { @@ -169,7 +169,7 @@ TEST_F(TestValues, SimpleCreate) EXPECT_EQ(AmqpValueType::Uuid, value.GetType()); EXPECT_EQ(uuid.ToString(), static_cast(value).ToString()); EXPECT_TRUE(AmqpValue() < value); - EXPECT_ANY_THROW(static_cast(value)); + EXPECT_ANY_THROW((bool)static_cast(value)); } { From 1c70040525288e72bc53f732c075fcb5b81ffd3e Mon Sep 17 00:00:00 2001 From: Larry Osterman Date: Thu, 27 Apr 2023 16:36:02 -0700 Subject: [PATCH 17/17] clang fix 10 --- sdk/core/azure-core-amqp/test/ut/amqp_value_tests.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/core/azure-core-amqp/test/ut/amqp_value_tests.cpp b/sdk/core/azure-core-amqp/test/ut/amqp_value_tests.cpp index 0290f4e9de..bd7f71795d 100644 --- a/sdk/core/azure-core-amqp/test/ut/amqp_value_tests.cpp +++ b/sdk/core/azure-core-amqp/test/ut/amqp_value_tests.cpp @@ -160,7 +160,7 @@ TEST_F(TestValues, SimpleCreate) EXPECT_EQ(AmqpValueType::String, value.GetType()); EXPECT_EQ(std::string("Fred"), fredP); EXPECT_TRUE(AmqpValue() < value); - EXPECT_ANY_THROW((bool)static_cast(value)); + EXPECT_ANY_THROW((void)static_cast(value)); } { @@ -169,7 +169,7 @@ TEST_F(TestValues, SimpleCreate) EXPECT_EQ(AmqpValueType::Uuid, value.GetType()); EXPECT_EQ(uuid.ToString(), static_cast(value).ToString()); EXPECT_TRUE(AmqpValue() < value); - EXPECT_ANY_THROW((bool)static_cast(value)); + EXPECT_ANY_THROW((void)static_cast(value)); } {