[network-diag] introduce AnswerBuilder to manage answer messages (#12887)

This commit introduces `AnswerBuilder` class to track and manage
Network Diagnostic answer messages. This class is used when the
response to a query requires multiple CoAP answer messages. It
automatically manages the inclusion of the Query ID and the Answer
TLVs(providing message indexing and "more-to-follow" flags) in each
allocated answer message, while maintaining all answer messages in a
queue. The `NetworkDiagnostic::Server` is updated to use the
`AnswerBuilder`, simplifying the logic for preparing and sending
answers.

The `AnswerBuilder` class is added in a new header file
`network_diagnostic_types.hpp` to allow for its reuse by other
modules in the future.
This commit is contained in:
Abtin Keshavarzian
2026-04-17 11:21:59 -07:00
committed by GitHub
parent 254043deec
commit 7eb59f71da
9 changed files with 380 additions and 113 deletions
+2
View File
@@ -755,6 +755,8 @@ openthread_core_files = [
"thread/network_diagnostic.hpp",
"thread/network_diagnostic_tlvs.cpp",
"thread/network_diagnostic_tlvs.hpp",
"thread/network_diagnostic_types.cpp",
"thread/network_diagnostic_types.hpp",
"thread/panid_query_server.cpp",
"thread/panid_query_server.hpp",
"thread/peer.cpp",
+1
View File
@@ -268,6 +268,7 @@ set(COMMON_SOURCES
thread/network_data_types.cpp
thread/network_diagnostic.cpp
thread/network_diagnostic_tlvs.cpp
thread/network_diagnostic_types.cpp
thread/panid_query_server.cpp
thread/peer.cpp
thread/peer_table.cpp
+24
View File
@@ -980,6 +980,30 @@ void MessageQueue::DequeueAndFreeAll(void)
}
}
void MessageQueue::EnqueueAllFrom(MessageQueue &aOtherQueue)
{
VerifyOrExit(&aOtherQueue != this);
VerifyOrExit(aOtherQueue.GetHead() != nullptr);
if (GetHead() == nullptr)
{
SetHead(aOtherQueue.GetHead());
}
else
{
GetTail()->Next() = aOtherQueue.GetHead();
aOtherQueue.GetHead()->Prev() = GetTail();
}
SetTail(aOtherQueue.GetTail());
aOtherQueue.Clear();
exit:
return;
}
Message::Iterator MessageQueue::begin(void) { return Message::Iterator(GetHead()); }
Message::ConstIterator MessageQueue::begin(void) const { return Message::ConstIterator(GetHead()); }
+9
View File
@@ -1731,6 +1731,15 @@ public:
*/
void DequeueAndFreeAll(void);
/**
* Enqueues all messages from another message queue at the end of this queue.
*
* Upon return, @p aOtherQueue will be empty.
*
* @param[in,out] aOtherQueue The other message queue to enqueue from.
*/
void EnqueueAllFrom(MessageQueue &aOtherQueue);
/**
* Gets the information about number of messages and buffers in the queue.
*
+36 -86
View File
@@ -539,34 +539,6 @@ exit:
#if OPENTHREAD_FTD
Error Server::AllocateAnswer(Coap::Message *&aAnswer, AnswerInfo &aInfo)
{
// Allocate an `Answer` message, adds it in `mAnswerQueue`,
// update the `aInfo.mFirstAnswer` if it is the first allocated
// messages, and appends `QueryIdTlv` to the message (if needed).
Error error = kErrorNone;
aAnswer = Get<Tmf::Agent>().AllocateAndInitConfirmablePostMessage(kUriDiagnosticGetAnswer);
VerifyOrExit(aAnswer != nullptr, error = kErrorNoBufs);
IgnoreError(aAnswer->SetPriority(aInfo.mPriority));
mAnswerQueue.Enqueue(*aAnswer);
if (aInfo.mFirstAnswer == nullptr)
{
aInfo.mFirstAnswer = aAnswer;
}
if (aInfo.mHasQueryId)
{
SuccessOrExit(error = Tlv::Append<QueryIdTlv>(*aAnswer, aInfo.mQueryId));
}
exit:
return error;
}
bool Server::IsLastAnswer(const Coap::Message &aAnswer) const
{
// Indicates whether `aAnswer` is the last one associated with
@@ -601,23 +573,29 @@ void Server::FreeAllRelatedAnswers(Coap::Message &aFirstAnswer)
}
}
void Server::PrepareAndSendAnswers(const Ip6::Address &aDestination, const Message &aRequest)
void Server::PrepareAndSendAnswers(const Ip6::Address &aDestination, const Coap::Message &aRequest)
{
AnswerBuilder answerBuilder(GetInstance());
Coap::Message *firstAnswer;
SuccessOrExit(PrepareAnswers(aRequest, answerBuilder));
firstAnswer = answerBuilder.GetAnswers().GetHead();
mAnswerQueue.EnqueueAllFrom(answerBuilder.GetAnswers());
SendNextAnswer(*firstAnswer, aDestination);
exit:
return;
}
Error Server::PrepareAnswers(const Coap::Message &aRequest, AnswerBuilder &aAnswerBuilder)
{
Coap::Message *answer;
Error error;
AnswerInfo info;
uint8_t tlvType;
TlvTypeListIterator iterator;
AnswerTlvValue answerTlvValue;
if (Tlv::Find<QueryIdTlv>(aRequest, info.mQueryId) == kErrorNone)
{
info.mHasQueryId = true;
}
info.mPriority = aRequest.GetPriority();
SuccessOrExit(error = AllocateAnswer(answer, info));
SuccessOrExit(error = aAnswerBuilder.Start(aRequest));
SuccessOrExit(error = iterator.InitForTypeListTlv(aRequest));
@@ -626,51 +604,23 @@ void Server::PrepareAndSendAnswers(const Ip6::Address &aDestination, const Messa
switch (tlvType)
{
case ChildTlv::kType:
SuccessOrExit(error = AppendChildTableAsChildTlvs(answer, info));
SuccessOrExit(error = AppendChildTableAsChildTlvs(aAnswerBuilder));
break;
case ChildIp6AddressListTlv::kType:
SuccessOrExit(error = AppendChildTableIp6AddressList(answer, info));
SuccessOrExit(error = AppendChildTableIp6AddressList(aAnswerBuilder));
break;
case RouterNeighborTlv::kType:
SuccessOrExit(error = AppendRouterNeighborTlvs(answer, info));
SuccessOrExit(error = AppendRouterNeighborTlvs(aAnswerBuilder));
break;
default:
SuccessOrExit(error = AppendDiagTlv(tlvType, *answer));
SuccessOrExit(error = AppendDiagTlv(tlvType, aAnswerBuilder.GetAnswer()));
break;
}
SuccessOrExit(error = CheckAnswerLength(answer, info));
SuccessOrExit(error = aAnswerBuilder.CheckAnswerLength());
}
answerTlvValue.Init(info.mAnswerIndex, AnswerTlvValue::kIsLast);
SuccessOrExit(error = Tlv::Append<AnswerTlv>(*answer, answerTlvValue));
SendNextAnswer(*info.mFirstAnswer, aDestination);
exit:
if ((error != kErrorNone) && (info.mFirstAnswer != nullptr))
{
FreeAllRelatedAnswers(*info.mFirstAnswer);
}
}
Error Server::CheckAnswerLength(Coap::Message *&aAnswer, AnswerInfo &aInfo)
{
// This method checks the length of the `aAnswer` message and if it
// is above the threshold, it enqueues the message for transmission
// after appending an Answer TLV with the current index to the
// message. In this case, it will also allocate a new answer
// message.
Error error = kErrorNone;
AnswerTlvValue answerTlvValue;
VerifyOrExit(aAnswer->GetLength() >= kAnswerMessageLengthThreshold);
answerTlvValue.Init(aInfo.mAnswerIndex++, AnswerTlvValue::kMoreToFollow);
SuccessOrExit(error = Tlv::Append<AnswerTlv>(*aAnswer, answerTlvValue));
error = AllocateAnswer(aAnswer, aInfo);
SuccessOrExit(error = aAnswerBuilder.Finish());
exit:
return error;
@@ -734,7 +684,7 @@ exit:
}
}
Error Server::AppendChildTableAsChildTlvs(Coap::Message *&aAnswer, AnswerInfo &aInfo)
Error Server::AppendChildTableAsChildTlvs(AnswerBuilder &aAnswerBuilder)
{
Error error = kErrorNone;
ChildTlvValue childTlvValue;
@@ -743,17 +693,17 @@ Error Server::AppendChildTableAsChildTlvs(Coap::Message *&aAnswer, AnswerInfo &a
{
childTlvValue.InitFrom(child);
SuccessOrExit(error = Tlv::Append<ChildTlv>(*aAnswer, childTlvValue));
SuccessOrExit(error = CheckAnswerLength(aAnswer, aInfo));
SuccessOrExit(error = Tlv::Append<ChildTlv>(aAnswerBuilder.GetAnswer(), childTlvValue));
SuccessOrExit(error = aAnswerBuilder.CheckAnswerLength());
}
error = Tlv::AppendEmpty<ChildTlv>(*aAnswer);
error = Tlv::AppendEmpty<ChildTlv>(aAnswerBuilder.GetAnswer());
exit:
return error;
}
Error Server::AppendRouterNeighborTlvs(Coap::Message *&aAnswer, AnswerInfo &aInfo)
Error Server::AppendRouterNeighborTlvs(AnswerBuilder &aAnswerBuilder)
{
Error error = kErrorNone;
RouterNeighborTlvValue neighborTlvValue;
@@ -767,27 +717,27 @@ Error Server::AppendRouterNeighborTlvs(Coap::Message *&aAnswer, AnswerInfo &aInf
neighborTlvValue.InitFrom(router);
SuccessOrExit(error = Tlv::Append<RouterNeighborTlv>(*aAnswer, neighborTlvValue));
SuccessOrExit(error = CheckAnswerLength(aAnswer, aInfo));
SuccessOrExit(error = Tlv::Append<RouterNeighborTlv>(aAnswerBuilder.GetAnswer(), neighborTlvValue));
SuccessOrExit(error = aAnswerBuilder.CheckAnswerLength());
}
error = Tlv::AppendEmpty<RouterNeighborTlv>(*aAnswer);
error = Tlv::AppendEmpty<RouterNeighborTlv>(aAnswerBuilder.GetAnswer());
exit:
return error;
}
Error Server::AppendChildTableIp6AddressList(Coap::Message *&aAnswer, AnswerInfo &aInfo)
Error Server::AppendChildTableIp6AddressList(AnswerBuilder &aAnswerBuilder)
{
Error error = kErrorNone;
for (const Child &child : Get<ChildTable>().Iterate(Child::kInStateValid))
{
SuccessOrExit(error = AppendChildIp6AddressListTlv(*aAnswer, child));
SuccessOrExit(error = CheckAnswerLength(aAnswer, aInfo));
SuccessOrExit(error = AppendChildIp6AddressListTlv(aAnswerBuilder.GetAnswer(), child));
SuccessOrExit(error = aAnswerBuilder.CheckAnswerLength());
}
error = Tlv::AppendEmpty<ChildIp6AddressListTlv>(*aAnswer);
error = Tlv::AppendEmpty<ChildIp6AddressListTlv>(aAnswerBuilder.GetAnswer());
exit:
return error;
+7 -27
View File
@@ -45,6 +45,7 @@
#include "common/numeric_limits.hpp"
#include "net/udp6.hpp"
#include "thread/network_diagnostic_tlvs.hpp"
#include "thread/network_diagnostic_types.hpp"
#include "thread/tmf.hpp"
#include "thread/uri_paths.hpp"
@@ -119,8 +120,7 @@ public:
}
private:
static constexpr uint16_t kMaxChildEntries = 398;
static constexpr uint16_t kAnswerMessageLengthThreshold = 800;
static constexpr uint16_t kMaxChildEntries = 398;
class TlvTypeListIterator
{
@@ -139,25 +139,6 @@ private:
BitSet<NumericLimits<uint8_t>::kMax + 1> mProcessedTlvs;
};
#if OPENTHREAD_FTD
struct AnswerInfo
{
AnswerInfo(void)
: mAnswerIndex(0)
, mQueryId(0)
, mHasQueryId(false)
, mFirstAnswer(nullptr)
{
}
uint16_t mAnswerIndex;
uint16_t mQueryId;
bool mHasQueryId;
Message::Priority mPriority;
Coap::Message *mFirstAnswer;
};
#endif
Error AppendDiagTlv(uint8_t aTlvType, Message &aMessage);
Error AppendIp6AddressList(Message &aMessage);
Error AppendRequestedTlvs(const Message &aRequest, Message &aResponse);
@@ -169,16 +150,15 @@ private:
#if OPENTHREAD_MTD
void SendAnswer(const Ip6::Address &aDestination, const Message &aRequest);
#elif OPENTHREAD_FTD
Error AllocateAnswer(Coap::Message *&aAnswer, AnswerInfo &aInfo);
Error CheckAnswerLength(Coap::Message *&aAnswer, AnswerInfo &aInfo);
bool IsLastAnswer(const Coap::Message &aAnswer) const;
void FreeAllRelatedAnswers(Coap::Message &aFirstAnswer);
void PrepareAndSendAnswers(const Ip6::Address &aDestination, const Message &aRequest);
void PrepareAndSendAnswers(const Ip6::Address &aDestination, const Coap::Message &aRequest);
Error PrepareAnswers(const Coap::Message &aRequest, AnswerBuilder &aAnswerBuilder);
void SendNextAnswer(Coap::Message &aAnswer, const Ip6::Address &aDestination);
Error AppendChildTable(Message &aMessage);
Error AppendChildTableAsChildTlvs(Coap::Message *&aAnswer, AnswerInfo &aInfo);
Error AppendRouterNeighborTlvs(Coap::Message *&aAnswer, AnswerInfo &aInfo);
Error AppendChildTableIp6AddressList(Coap::Message *&aAnswer, AnswerInfo &aInfo);
Error AppendChildTableAsChildTlvs(AnswerBuilder &aAnswerBuilder);
Error AppendRouterNeighborTlvs(AnswerBuilder &aAnswerBuilder);
Error AppendChildTableIp6AddressList(AnswerBuilder &aAnswerBuilder);
Error AppendChildIp6AddressListTlv(Message &aAnswer, const Child &aChild);
Error AppendEnhancedRoute(Message &aMessage);
@@ -0,0 +1,108 @@
/*
* Copyright (c) 2026, The OpenThread Authors.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holder nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/**
* @file
* This file implements methods for Network Diagnostics helper types and classes.
*/
#include "network_diagnostic_types.hpp"
#include "instance/instance.hpp"
namespace ot {
namespace NetworkDiagnostic {
AnswerBuilder::AnswerBuilder(Instance &aInstance)
: InstanceLocator(aInstance)
, mAnswer(nullptr)
, mAnswerIndex(0)
, mQueryId(0)
, mHasQueryId(false)
, mPriority(Message::kPriorityNormal)
{
}
AnswerBuilder::~AnswerBuilder(void) { mAnswers.DequeueAndFreeAll(); }
Error AnswerBuilder::Start(const Coap::Message &aRequest)
{
mAnswerIndex = 0;
mHasQueryId = (Tlv::Find<QueryIdTlv>(aRequest, mQueryId) == kErrorNone);
mPriority = aRequest.GetPriority();
return Allocate();
}
Error AnswerBuilder::Finish(void) { return AppendAnswerTlv(AnswerTlvValue::kIsLast); }
Error AnswerBuilder::Allocate(void)
{
Error error = kErrorNone;
mAnswer = Get<Tmf::Agent>().AllocateAndInitConfirmablePostMessage(kUriDiagnosticGetAnswer);
VerifyOrExit(mAnswer != nullptr, error = kErrorNoBufs);
IgnoreError(mAnswer->SetPriority(mPriority));
mAnswers.Enqueue(*mAnswer);
if (mHasQueryId)
{
SuccessOrExit(error = Tlv::Append<QueryIdTlv>(*mAnswer, mQueryId));
}
exit:
return error;
}
Error AnswerBuilder::AppendAnswerTlv(AnswerTlvValue::IsLastFlag aFlag)
{
AnswerTlvValue value;
value.Init(mAnswerIndex++, aFlag);
return Tlv::Append<AnswerTlv>(*mAnswer, value);
}
Error AnswerBuilder::CheckAnswerLength(void)
{
Error error = kErrorNone;
VerifyOrExit(mAnswer->GetLength() >= kAnswerMessageLengthThreshold);
SuccessOrExit(error = AppendAnswerTlv(AnswerTlvValue::kMoreToFollow));
error = Allocate();
exit:
return error;
}
} // namespace NetworkDiagnostic
} // namespace ot
@@ -0,0 +1,139 @@
/*
* Copyright (c) 2026, The OpenThread Authors.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holder nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/**
* @file
* This file includes definitions for Network Diagnostics helper types and classes.
*/
#ifndef OT_CORE_THREAD_NETWORK_DIAGNOSTIC_TYPES_HPP_
#define OT_CORE_THREAD_NETWORK_DIAGNOSTIC_TYPES_HPP_
#include "openthread-core-config.h"
#include "network_diagnostic_tlvs.hpp"
#include "coap/coap_message.hpp"
#include "common/error.hpp"
#include "common/locator.hpp"
#include "common/time.hpp"
#include "thread/uri_paths.hpp"
namespace ot {
namespace NetworkDiagnostic {
/**
* Represents an object for tracking and managing Network Diagnostic answer messages.
*
* This class is used when the response to a Network Diagnostic query requires multiple CoAP answer messages . It
* manages the inclusion of the Query ID and the Answer TLV (providing message indexing and "more-to-follow" flags)
* in each allocated answer message, while maintaining all answer messages in a queue.
*/
class AnswerBuilder : public InstanceLocator
{
public:
/**
* Initializes the `AnswerBuilder`.
*
* @param[in] aInstance The OpenThread instance.
*/
explicit AnswerBuilder(Instance &aInstance);
/**
* Destructor for `AnswerBuilder`.
*
* Any remaining messages in the answers queue are freed.
*/
~AnswerBuilder(void);
/**
* Starts the building of answer messages based on a given Network Diagnostic request.
*
* This method searches for a Query ID TLV within @p aRequest. If present, the Query ID is captured and
* automatically included in all allocated answer messages. It also captures the priority from @p aRequest and
* uses it for all answer messages.
*
* @param[in] aRequest The Network Diagnostic request message.
*
* @retval kErrorNone Successfully started.
* @retval kErrorNoBufs Insufficient message buffers to allocate the first answer.
*/
Error Start(const Coap::Message &aRequest);
/**
* Gets the current answer message.
*
* @returns The current answer message.
*/
Coap::Message &GetAnswer(void) { return *mAnswer; }
/**
* Checks the current answer message length and allocates a new one if it exceeds the threshold.
*
* If the current answer message length is above the threshold, this method appends an Answer TLV with the
* "more to follow" flag set, and then allocates a new answer message, adding it to the answer queue.
*
* @retval kErrorNone Successfully checked and handled (possibly allocating a new message).
* @retval kErrorNoBufs Insufficient message buffers to append Answer TLV or allocate a new message.
*/
Error CheckAnswerLength(void);
/**
* Finishes the answer generation.
*
* This method appends an Answer TLV to the current answer message with the "is last" flag set.
*
* @retval kErrorNone Successfully finished.
* @retval kErrorNoBufs Insufficient message buffers to append the Answer TLV.
*/
Error Finish(void);
/**
* Gets the queue containing all answer messages.
*
* @returns The message queue containing all answer messages.
*/
Coap::MessageQueue &GetAnswers(void) { return mAnswers; }
private:
static constexpr uint16_t kAnswerMessageLengthThreshold = 800;
Error Allocate(void);
Error AppendAnswerTlv(AnswerTlvValue::IsLastFlag aFlag);
Coap::Message *mAnswer;
Coap::MessageQueue mAnswers;
uint16_t mAnswerIndex;
uint16_t mQueryId;
bool mHasQueryId;
Message::Priority mPriority;
};
} // namespace NetworkDiagnostic
} // namespace ot
#endif // OT_CORE_THREAD_NETWORK_DIAGNOSTIC_TYPES_HPP_
+54
View File
@@ -286,6 +286,59 @@ void TestMessageQueue(void)
testFreeInstance(sInstance);
}
void TestMessageQueueEnqueueAllFrom(void)
{
// Validates the behavior of `MessageQueue::EnqueueAllFrom()` method.
MessageQueue messageQueue1;
MessageQueue messageQueue2;
Message *messages[kNumTestMessages];
sInstance = testInitInstance();
VerifyOrQuit(sInstance != nullptr);
sMessagePool = &sInstance->Get<MessagePool>();
for (Message *&msg : messages)
{
msg = sMessagePool->Allocate(Message::kTypeIp6);
VerifyOrQuit(msg != nullptr, "Message::Allocate() failed");
}
// Case 1: Both queues are empty
messageQueue1.EnqueueAllFrom(messageQueue2);
VerifyMessageQueueContent(messageQueue1, 0);
VerifyMessageQueueContent(messageQueue2, 0);
// Case 2: Target queue (Q1) is empty, input queue (Q2) is non-empty
messageQueue2.Enqueue(*messages[0]);
messageQueue2.Enqueue(*messages[1]);
VerifyMessageQueueContent(messageQueue2, 2, messages[0], messages[1]);
messageQueue1.EnqueueAllFrom(messageQueue2);
VerifyMessageQueueContent(messageQueue1, 2, messages[0], messages[1]);
VerifyMessageQueueContent(messageQueue2, 0);
// Case 3: Target queue (Q1) is non-empty, input queue (Q2) is empty
messageQueue1.EnqueueAllFrom(messageQueue2);
VerifyMessageQueueContent(messageQueue1, 2, messages[0], messages[1]);
VerifyMessageQueueContent(messageQueue2, 0);
// Case 4: Both queues are non-empty
messageQueue2.Enqueue(*messages[2]);
VerifyMessageQueueContent(messageQueue2, 1, messages[2]);
messageQueue1.EnqueueAllFrom(messageQueue2);
VerifyMessageQueueContent(messageQueue1, 3, messages[0], messages[1], messages[2]);
VerifyMessageQueueContent(messageQueue2, 0);
// Case 5: Using the same queue as both the target and input queues
messageQueue1.EnqueueAllFrom(messageQueue1);
VerifyMessageQueueContent(messageQueue1, 3, messages[0], messages[1], messages[2]);
messageQueue2.EnqueueAllFrom(messageQueue2);
VerifyMessageQueueContent(messageQueue2, 0);
testFreeInstance(sInstance);
}
// This function verifies the content of the message queue to match the passed in messages
void VerifyMessageQueueContentUsingOtApi(otMessageQueue *aQueue, int aExpectedLength, ...)
{
@@ -386,6 +439,7 @@ void TestMessageQueueOtApis(void)
int main(void)
{
ot::TestMessageQueue();
ot::TestMessageQueueEnqueueAllFrom();
ot::TestMessageQueueOtApis();
printf("All tests passed\n");
return 0;