Switch a few more unit tests to async message delivery. (#12482)
* Create a LoopbackMessagingContext that supports async operation.
Trying to push the setup that AppContext has to a wider range of tests
* Convert TestExchangeMgr to LoopbackMessagingContext.
* Switch TestExchangeMgr to async message delivery.
* Convert TestPASESession to LoopbackMessagingContext.
* Convert TestPASESession to async message delivery.
diff --git a/src/app/tests/AppTestContext.cpp b/src/app/tests/AppTestContext.cpp
index ee4eee0..970e44d 100644
--- a/src/app/tests/AppTestContext.cpp
+++ b/src/app/tests/AppTestContext.cpp
@@ -25,10 +25,7 @@
CHIP_ERROR AppContext::Init()
{
- ReturnErrorOnFailure(chip::Platform::MemoryInit());
- ReturnErrorOnFailure(mIOContext.Init());
- ReturnErrorOnFailure(mTransportManager.Init("LOOPBACK"));
- ReturnErrorOnFailure(MessagingContext::Init(&mTransportManager, &mIOContext));
+ ReturnErrorOnFailure(Super::Init());
ReturnErrorOnFailure(chip::app::InteractionModelEngine::GetInstance()->Init(&GetExchangeManager(), nullptr));
return CHIP_NO_ERROR;
@@ -37,9 +34,7 @@
CHIP_ERROR AppContext::Shutdown()
{
chip::app::InteractionModelEngine::GetInstance()->Shutdown();
- ReturnErrorOnFailure(MessagingContext::Shutdown());
- ReturnErrorOnFailure(mIOContext.Shutdown());
- chip::Platform::MemoryShutdown();
+ ReturnErrorOnFailure(Super::Shutdown());
return CHIP_NO_ERROR;
}
diff --git a/src/app/tests/AppTestContext.h b/src/app/tests/AppTestContext.h
index d12e0c9..96ce876 100644
--- a/src/app/tests/AppTestContext.h
+++ b/src/app/tests/AppTestContext.h
@@ -15,12 +15,9 @@
*/
#pragma once
-#include "system/SystemClock.h"
#include <messaging/tests/MessagingContext.h>
#include <transport/raw/tests/NetworkTestHelpers.h>
-#include <nlunit-test.h>
-
namespace chip {
namespace Test {
@@ -28,83 +25,16 @@
* @brief The context of test cases for messaging layer. It wil initialize network layer and system layer, and create
* two secure sessions, connected with each other. Exchanges can be created for each secure session.
*/
-class AppContext : public MessagingContext
+class AppContext : public LoopbackMessagingContext<LoopbackTransport>
{
+ typedef LoopbackMessagingContext<LoopbackTransport> Super;
+
public:
- /// Initialize the underlying layers and test suite pointer
- CHIP_ERROR Init();
+ /// Initialize the underlying layers.
+ CHIP_ERROR Init() override;
// Shutdown all layers, finalize operations
- CHIP_ERROR Shutdown();
-
- /*
- * For unit-tests that simulate end-to-end transmission and reception of messages in loopback mode,
- * this mode better replicates a real-functioning stack that correctly handles the processing
- * of a transmitted message as an asynchronous, bottom half handler dispatched after the current execution context has
- completed.
- * This is achieved using SystemLayer::ScheduleWork.
-
- * This should be used in conjunction with the DrainAndServiceIO function below to correctly service and drain the event queue.
- *
- */
- void EnableAsyncDispatch()
- {
- auto & impl = mTransportManager.GetTransport().GetImplAtIndex<0>();
- impl.EnableAsyncDispatch(&mIOContext.GetSystemLayer());
- }
-
- /*
- * This drives the servicing of events using the embedded IOContext while there are pending
- * messages in the loopback transport's pending message queue. This should run to completion
- * in well-behaved logic (i.e there isn't an indefinite ping-pong of messages transmitted back
- * and forth).
- *
- * Consequently, this is guarded with a user-provided timeout to ensure we don't have unit-tests that stall
- * in CI due to bugs in the code that is being tested.
- *
- * This DOES NOT ensure that all pending events are serviced to completion (i.e timers, any ScheduleWork calls).
- *
- */
- void DrainAndServiceIO(System::Clock::Timeout maxWait = chip::System::Clock::Seconds16(5))
- {
- auto & impl = mTransportManager.GetTransport().GetImplAtIndex<0>();
- System::Clock::Timestamp startTime = System::SystemClock().GetMonotonicTimestamp();
-
- while (impl.HasPendingMessages())
- {
- mIOContext.DriveIO();
- if ((System::SystemClock().GetMonotonicTimestamp() - startTime) >= maxWait)
- {
- break;
- }
- }
- }
-
- static int Initialize(void * context)
- {
- auto * ctx = static_cast<AppContext *>(context);
- return ctx->Init() == CHIP_NO_ERROR ? SUCCESS : FAILURE;
- }
-
- static int InitializeAsync(void * context)
- {
- auto * ctx = static_cast<AppContext *>(context);
-
- VerifyOrReturnError(ctx->Init() == CHIP_NO_ERROR, FAILURE);
- ctx->EnableAsyncDispatch();
-
- return SUCCESS;
- }
-
- static int Finalize(void * context)
- {
- auto * ctx = static_cast<AppContext *>(context);
- return ctx->Shutdown() == CHIP_NO_ERROR ? SUCCESS : FAILURE;
- }
-
-private:
- chip::TransportMgr<chip::Test::LoopbackTransport> mTransportManager;
- chip::Test::IOContext mIOContext;
+ CHIP_ERROR Shutdown() override;
};
} // namespace Test
diff --git a/src/messaging/tests/MessagingContext.h b/src/messaging/tests/MessagingContext.h
index 0e91596..0435097 100644
--- a/src/messaging/tests/MessagingContext.h
+++ b/src/messaging/tests/MessagingContext.h
@@ -20,10 +20,13 @@
#include <messaging/ExchangeMgr.h>
#include <protocols/secure_channel/MessageCounterManager.h>
#include <protocols/secure_channel/PASESession.h>
+#include <system/SystemClock.h>
#include <transport/SessionManager.h>
#include <transport/TransportMgr.h>
#include <transport/raw/tests/NetworkTestHelpers.h>
+#include <nlunit-test.h>
+
namespace chip {
namespace Test {
@@ -109,5 +112,104 @@
FabricIndex mDestFabricIndex = 0;
};
+template <typename Transport>
+class LoopbackMessagingContext : public MessagingContext
+{
+public:
+ virtual ~LoopbackMessagingContext() {}
+
+ /// Initialize the underlying layers.
+ virtual CHIP_ERROR Init()
+ {
+ ReturnErrorOnFailure(chip::Platform::MemoryInit());
+ ReturnErrorOnFailure(mIOContext.Init());
+ ReturnErrorOnFailure(mTransportManager.Init("LOOPBACK"));
+ ReturnErrorOnFailure(MessagingContext::Init(&mTransportManager, &mIOContext));
+ return CHIP_NO_ERROR;
+ }
+
+ // Shutdown all layers, finalize operations
+ virtual CHIP_ERROR Shutdown()
+ {
+ ReturnErrorOnFailure(MessagingContext::Shutdown());
+ ReturnErrorOnFailure(mIOContext.Shutdown());
+ chip::Platform::MemoryShutdown();
+ return CHIP_NO_ERROR;
+ }
+
+ // Init/Shutdown Helpers that can be used directly as the nlTestSuite
+ // initialize/finalize function.
+ static int Initialize(void * context)
+ {
+ auto * ctx = static_cast<LoopbackMessagingContext *>(context);
+ return ctx->Init() == CHIP_NO_ERROR ? SUCCESS : FAILURE;
+ }
+
+ static int InitializeAsync(void * context)
+ {
+ auto * ctx = static_cast<LoopbackMessagingContext *>(context);
+
+ VerifyOrReturnError(ctx->Init() == CHIP_NO_ERROR, FAILURE);
+ ctx->EnableAsyncDispatch();
+
+ return SUCCESS;
+ }
+
+ static int Finalize(void * context)
+ {
+ auto * ctx = static_cast<LoopbackMessagingContext *>(context);
+ return ctx->Shutdown() == CHIP_NO_ERROR ? SUCCESS : FAILURE;
+ }
+
+ Transport & GetLoopback() { return mTransportManager.GetTransport().template GetImplAtIndex<0>(); }
+
+ /*
+ * For unit-tests that simulate end-to-end transmission and reception of messages in loopback mode,
+ * this mode better replicates a real-functioning stack that correctly handles the processing
+ * of a transmitted message as an asynchronous, bottom half handler dispatched after the current execution context has
+ completed.
+ * This is achieved using SystemLayer::ScheduleWork.
+
+ * This should be used in conjunction with the DrainAndServiceIO function below to correctly service and drain the event queue.
+ *
+ */
+ void EnableAsyncDispatch()
+ {
+ auto & impl = GetLoopback();
+ impl.EnableAsyncDispatch(&mIOContext.GetSystemLayer());
+ }
+
+ /*
+ * This drives the servicing of events using the embedded IOContext while there are pending
+ * messages in the loopback transport's pending message queue. This should run to completion
+ * in well-behaved logic (i.e there isn't an indefinite ping-pong of messages transmitted back
+ * and forth).
+ *
+ * Consequently, this is guarded with a user-provided timeout to ensure we don't have unit-tests that stall
+ * in CI due to bugs in the code that is being tested.
+ *
+ * This DOES NOT ensure that all pending events are serviced to completion (i.e timers, any ScheduleWork calls).
+ *
+ */
+ void DrainAndServiceIO(System::Clock::Timeout maxWait = chip::System::Clock::Seconds16(5))
+ {
+ auto & impl = GetLoopback();
+ System::Clock::Timestamp startTime = System::SystemClock().GetMonotonicTimestamp();
+
+ while (impl.HasPendingMessages())
+ {
+ mIOContext.DriveIO();
+ if ((System::SystemClock().GetMonotonicTimestamp() - startTime) >= maxWait)
+ {
+ break;
+ }
+ }
+ }
+
+private:
+ TransportMgr<Transport> mTransportManager;
+ Test::IOContext mIOContext;
+};
+
} // namespace Test
} // namespace chip
diff --git a/src/messaging/tests/TestExchangeMgr.cpp b/src/messaging/tests/TestExchangeMgr.cpp
index 52ee299..1ac634b 100644
--- a/src/messaging/tests/TestExchangeMgr.cpp
+++ b/src/messaging/tests/TestExchangeMgr.cpp
@@ -49,7 +49,7 @@
using namespace chip::Transport;
using namespace chip::Messaging;
-using TestContext = chip::Test::MessagingContext;
+using TestContext = chip::Test::LoopbackMessagingContext<Test::LoopbackTransport>;
enum : uint8_t
{
@@ -59,9 +59,6 @@
TestContext sContext;
-TransportMgr<Test::LoopbackTransport> gTransportMgr;
-Test::IOContext gIOContext;
-
class MockAppDelegate : public ExchangeDelegate
{
public:
@@ -134,6 +131,8 @@
err = ec1->SendMessage(Protocols::BDX::Id, kMsgType_TEST1, System::PacketBufferHandle::New(System::PacketBuffer::kMaxSize),
SendFlags(Messaging::SendMessageFlags::kNoAutoRequestAck));
NL_TEST_ASSERT(inSuite, err != CHIP_NO_ERROR);
+ ctx.DrainAndServiceIO();
+
NL_TEST_ASSERT(inSuite, !receiveDelegate.IsOnMessageReceivedCalled);
ec1->Close();
@@ -150,6 +149,8 @@
ec1->SendMessage(Protocols::BDX::Id, kMsgType_TEST1, System::PacketBufferHandle::New(System::PacketBuffer::kMaxSize),
SendFlags(Messaging::SendMessageFlags::kExpectResponse).Set(Messaging::SendMessageFlags::kNoAutoRequestAck));
+
+ ctx.DrainAndServiceIO();
NL_TEST_ASSERT(inSuite, !sendDelegate.IsOnResponseTimeoutCalled);
// Expire the session this exchange is supposedly on. This should close the
@@ -203,6 +204,8 @@
// send a malicious packet
ec1->SendMessage(Protocols::BDX::Id, kMsgType_TEST2, System::PacketBufferHandle::New(System::PacketBuffer::kMaxSize),
SendFlags(Messaging::SendMessageFlags::kNoAutoRequestAck));
+
+ ctx.DrainAndServiceIO();
NL_TEST_ASSERT(inSuite, !mockUnsolicitedAppDelegate.IsOnMessageReceivedCalled);
ec1 = ctx.NewExchangeToAlice(&mockSolicitedAppDelegate);
@@ -210,6 +213,8 @@
// send a good packet
ec1->SendMessage(Protocols::BDX::Id, kMsgType_TEST1, System::PacketBufferHandle::New(System::PacketBuffer::kMaxSize),
SendFlags(Messaging::SendMessageFlags::kNoAutoRequestAck));
+
+ ctx.DrainAndServiceIO();
NL_TEST_ASSERT(inSuite, mockUnsolicitedAppDelegate.IsOnMessageReceivedCalled);
err = ctx.GetExchangeManager().UnregisterUnsolicitedMessageHandlerForType(Protocols::BDX::Id, kMsgType_TEST1);
@@ -234,46 +239,16 @@
};
// clang-format on
-int Initialize(void * aContext);
-int Finalize(void * aContext);
-
// clang-format off
nlTestSuite sSuite =
{
"Test-CHIP-ExchangeManager",
&sTests[0],
- Initialize,
- Finalize
+ TestContext::InitializeAsync,
+ TestContext::Finalize
};
// clang-format on
-/**
- * Initialize the test suite.
- */
-int Initialize(void * aContext)
-{
- // Initialize System memory and resources
- VerifyOrReturnError(chip::Platform::MemoryInit() == CHIP_NO_ERROR, FAILURE);
- VerifyOrReturnError(gIOContext.Init() == CHIP_NO_ERROR, FAILURE);
- VerifyOrReturnError(gTransportMgr.Init("LOOPBACK") == CHIP_NO_ERROR, FAILURE);
-
- auto * ctx = static_cast<TestContext *>(aContext);
- VerifyOrReturnError(ctx->Init(&gTransportMgr, &gIOContext) == CHIP_NO_ERROR, FAILURE);
-
- return SUCCESS;
-}
-
-/**
- * Finalize the test suite.
- */
-int Finalize(void * aContext)
-{
- CHIP_ERROR err = reinterpret_cast<TestContext *>(aContext)->Shutdown();
- gIOContext.Shutdown();
- chip::Platform::MemoryShutdown();
- return (err == CHIP_NO_ERROR) ? SUCCESS : FAILURE;
-}
-
} // namespace
/**
diff --git a/src/protocols/secure_channel/tests/TestPASESession.cpp b/src/protocols/secure_channel/tests/TestPASESession.cpp
index a3d2316..befcd1f 100644
--- a/src/protocols/secure_channel/tests/TestPASESession.cpp
+++ b/src/protocols/secure_channel/tests/TestPASESession.cpp
@@ -41,7 +41,7 @@
using namespace chip::Messaging;
using namespace chip::Protocols;
-using TestContext = chip::Test::MessagingContext;
+namespace {
class PASETestLoopbackTransport : public Test::LoopbackTransport
{
@@ -50,19 +50,13 @@
public:
bool CanSendToPeer(const PeerAddress & address) override { return true; }
- void Reset()
- {
- Test::LoopbackTransport::Reset();
- mContext = nullptr;
- }
-
- TestContext * mContext = nullptr;
- bool mMessageDropped = false;
+ bool mMessageDropped = false;
};
-TransportMgrBase gTransportMgr;
-PASETestLoopbackTransport gLoopback;
-chip::Test::IOContext gIOContext;
+using TestContext = chip::Test::LoopbackMessagingContext<PASETestLoopbackTransport>;
+
+TestContext sContext;
+auto & gLoopback = sContext.GetLoopback();
class TestSecurePairingDelegate : public SessionEstablishmentDelegate
{
@@ -91,6 +85,8 @@
void SecurePairingWaitTest(nlTestSuite * inSuite, void * inContext)
{
+ TestContext & ctx = *reinterpret_cast<TestContext *>(inContext);
+
// Test all combinations of invalid parameters
TestSecurePairingDelegate delegate;
PASESession pairing;
@@ -103,11 +99,16 @@
gLoopback.Reset();
NL_TEST_ASSERT(inSuite, pairing.WaitForPairing(1234, 500, ByteSpan(nullptr, 0), 0, &delegate) == CHIP_ERROR_INVALID_ARGUMENT);
+ ctx.DrainAndServiceIO();
+
NL_TEST_ASSERT(inSuite,
pairing.WaitForPairing(1234, 500, ByteSpan((const uint8_t *) "saltSalt", 8), 0, nullptr) ==
CHIP_ERROR_INVALID_ARGUMENT);
+ ctx.DrainAndServiceIO();
+
NL_TEST_ASSERT(inSuite,
pairing.WaitForPairing(1234, 500, ByteSpan((const uint8_t *) "saltSalt", 8), 0, &delegate) == CHIP_NO_ERROR);
+ ctx.DrainAndServiceIO();
}
void SecurePairingStartTest(nlTestSuite * inSuite, void * inContext)
@@ -130,6 +131,7 @@
gLoopback.Reset();
NL_TEST_ASSERT(inSuite,
pairing.Pair(Transport::PeerAddress(Transport::Type::kBle), 1234, 0, context, &delegate) == CHIP_NO_ERROR);
+ ctx.DrainAndServiceIO();
NL_TEST_ASSERT(inSuite, gLoopback.mSentMessageCount == 1);
@@ -148,6 +150,8 @@
NL_TEST_ASSERT(inSuite,
pairing1.Pair(Transport::PeerAddress(Transport::Type::kBle), 1234, 0, context1, &delegate) ==
CHIP_ERROR_BAD_REQUEST);
+ ctx.DrainAndServiceIO();
+
gLoopback.mMessageSendError = CHIP_NO_ERROR;
}
@@ -178,7 +182,6 @@
64_ms32, // CHIP_CONFIG_MRP_DEFAULT_IDLE_RETRY_INTERVAL
64_ms32, // CHIP_CONFIG_MRP_DEFAULT_ACTIVE_RETRY_INTERVAL
});
- gLoopback.mContext = &ctx;
}
NL_TEST_ASSERT(inSuite,
@@ -188,15 +191,19 @@
NL_TEST_ASSERT(inSuite,
pairingAccessory.WaitForPairing(1234, 500, ByteSpan((const uint8_t *) "saltSALTsaltSALT", 16), 0,
&delegateAccessory) == CHIP_NO_ERROR);
+ ctx.DrainAndServiceIO();
+
NL_TEST_ASSERT(inSuite,
pairingCommissioner.Pair(Transport::PeerAddress(Transport::Type::kBle), 1234, 0, contextCommissioner,
&delegateCommissioner) == CHIP_NO_ERROR);
+ ctx.DrainAndServiceIO();
while (gLoopback.mMessageDropped)
{
chip::test_utils::SleepMillis(65);
gLoopback.mMessageDropped = false;
ReliableMessageMgr::Timeout(&ctx.GetSystemLayer(), ctx.GetExchangeManager().GetReliableMessageMgr());
+ ctx.DrainAndServiceIO();
};
// Standalone acks also increment the mSentMessageCount. But some messages could be acked
@@ -206,7 +213,6 @@
NL_TEST_ASSERT(inSuite, gLoopback.mSentMessageCount >= 5);
NL_TEST_ASSERT(inSuite, delegateAccessory.mNumPairingComplete == 1);
NL_TEST_ASSERT(inSuite, delegateCommissioner.mNumPairingComplete == 1);
- gLoopback.mContext = nullptr;
}
void SecurePairingHandshakeTest(nlTestSuite * inSuite, void * inContext)
@@ -256,7 +262,6 @@
64_ms32, // CHIP_CONFIG_MRP_DEFAULT_IDLE_RETRY_INTERVAL
64_ms32, // CHIP_CONFIG_MRP_DEFAULT_ACTIVE_RETRY_INTERVAL
});
- gLoopback.mContext = &ctx;
NL_TEST_ASSERT(inSuite,
ctx.GetExchangeManager().RegisterUnsolicitedMessageHandlerForType(
@@ -265,11 +270,13 @@
NL_TEST_ASSERT(inSuite,
pairingAccessory.WaitForPairing(1234, 500, ByteSpan((const uint8_t *) "saltSALTsaltSALT", 16), 0,
&delegateAccessory) == CHIP_NO_ERROR);
+ ctx.DrainAndServiceIO();
+
NL_TEST_ASSERT(inSuite,
pairingCommissioner.Pair(Transport::PeerAddress(Transport::Type::kBle), 4321, 0, contextCommissioner,
&delegateCommissioner) == CHIP_NO_ERROR);
+ ctx.DrainAndServiceIO();
- gLoopback.mContext = nullptr;
NL_TEST_ASSERT(inSuite, delegateAccessory.mNumPairingComplete == 0);
NL_TEST_ASSERT(inSuite, delegateAccessory.mNumPairingErrors == 1);
NL_TEST_ASSERT(inSuite, delegateCommissioner.mNumPairingComplete == 0);
@@ -371,8 +378,6 @@
};
// clang-format on
-static TestContext sContext;
-
// clang-format on
//
/**
@@ -381,21 +386,15 @@
int TestSecurePairing_Setup(void * inContext)
{
// Initialize System memory and resources
- VerifyOrReturnError(chip::Platform::MemoryInit() == CHIP_NO_ERROR, FAILURE);
- VerifyOrReturnError(gIOContext.Init() == CHIP_NO_ERROR, FAILURE);
- VerifyOrReturnError(gTransportMgr.Init(&gLoopback) == CHIP_NO_ERROR, FAILURE);
+ VerifyOrReturnError(TestContext::InitializeAsync(inContext) == SUCCESS, FAILURE);
auto & ctx = *static_cast<TestContext *>(inContext);
- VerifyOrReturnError(ctx.Init(&gTransportMgr, &gIOContext) == CHIP_NO_ERROR, FAILURE);
-
ctx.SetBobNodeId(kPlaceholderNodeId);
ctx.SetAliceNodeId(kPlaceholderNodeId);
ctx.SetBobKeyId(0);
ctx.SetAliceKeyId(0);
ctx.SetFabricIndex(kUndefinedFabricIndex);
- gTransportMgr.SetSessionManager(&ctx.GetSecureSessionManager());
-
return SUCCESS;
}
@@ -404,13 +403,10 @@
*/
int TestSecurePairing_Teardown(void * inContext)
{
- CHIP_ERROR err = reinterpret_cast<TestContext *>(inContext)->Shutdown();
- gIOContext.Shutdown();
- chip::Platform::MemoryShutdown();
- return (err == CHIP_NO_ERROR) ? SUCCESS : FAILURE;
+ return TestContext::Finalize(inContext);
}
-// TODO: TestPASESession is currently disabled due to lacking convenient way of creating exchange context
+} // anonymous namespace
/**
* Main