diff --git a/src/app/tests/AppTestContext.cpp b/src/app/tests/AppTestContext.cpp index ee4eee0c07ba40..970e44d280e4c0 100644 --- a/src/app/tests/AppTestContext.cpp +++ b/src/app/tests/AppTestContext.cpp @@ -25,10 +25,7 @@ namespace Test { 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::Init() 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 d12e0c98346a6d..96ce8769a53201 100644 --- a/src/app/tests/AppTestContext.h +++ b/src/app/tests/AppTestContext.h @@ -15,12 +15,9 @@ */ #pragma once -#include "system/SystemClock.h" #include #include -#include - namespace chip { namespace Test { @@ -28,83 +25,16 @@ namespace Test { * @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 { + typedef LoopbackMessagingContext 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(context); - return ctx->Init() == CHIP_NO_ERROR ? SUCCESS : FAILURE; - } - - static int InitializeAsync(void * context) - { - auto * ctx = static_cast(context); - - VerifyOrReturnError(ctx->Init() == CHIP_NO_ERROR, FAILURE); - ctx->EnableAsyncDispatch(); - - return SUCCESS; - } - - static int Finalize(void * context) - { - auto * ctx = static_cast(context); - return ctx->Shutdown() == CHIP_NO_ERROR ? SUCCESS : FAILURE; - } - -private: - chip::TransportMgr 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 0e9159648fd135..043509781e9e2d 100644 --- a/src/messaging/tests/MessagingContext.h +++ b/src/messaging/tests/MessagingContext.h @@ -20,10 +20,13 @@ #include #include #include +#include #include #include #include +#include + namespace chip { namespace Test { @@ -109,5 +112,104 @@ class MessagingContext FabricIndex mDestFabricIndex = 0; }; +template +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(context); + return ctx->Init() == CHIP_NO_ERROR ? SUCCESS : FAILURE; + } + + static int InitializeAsync(void * context) + { + auto * ctx = static_cast(context); + + VerifyOrReturnError(ctx->Init() == CHIP_NO_ERROR, FAILURE); + ctx->EnableAsyncDispatch(); + + return SUCCESS; + } + + static int Finalize(void * context) + { + auto * ctx = static_cast(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 mTransportManager; + Test::IOContext mIOContext; +}; + } // namespace Test } // namespace chip diff --git a/src/messaging/tests/TestExchangeMgr.cpp b/src/messaging/tests/TestExchangeMgr.cpp index 52ee2995a161d3..1ac634be96353b 100644 --- a/src/messaging/tests/TestExchangeMgr.cpp +++ b/src/messaging/tests/TestExchangeMgr.cpp @@ -49,7 +49,7 @@ using namespace chip::Inet; using namespace chip::Transport; using namespace chip::Messaging; -using TestContext = chip::Test::MessagingContext; +using TestContext = chip::Test::LoopbackMessagingContext; enum : uint8_t { @@ -59,9 +59,6 @@ enum : uint8_t TestContext sContext; -TransportMgr gTransportMgr; -Test::IOContext gIOContext; - class MockAppDelegate : public ExchangeDelegate { public: @@ -134,6 +131,8 @@ void CheckSessionExpirationBasics(nlTestSuite * inSuite, void * inContext) 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 @@ void CheckSessionExpirationTimeout(nlTestSuite * inSuite, void * inContext) 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 @@ void CheckExchangeMessages(nlTestSuite * inSuite, void * inContext) // 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 @@ void CheckExchangeMessages(nlTestSuite * inSuite, void * inContext) // 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 @@ const nlTest sTests[] = }; // 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(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(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 a3d2316de750b3..befcd1f292e486 100644 --- a/src/protocols/secure_channel/tests/TestPASESession.cpp +++ b/src/protocols/secure_channel/tests/TestPASESession.cpp @@ -41,7 +41,7 @@ using namespace chip::Transport; using namespace chip::Messaging; using namespace chip::Protocols; -using TestContext = chip::Test::MessagingContext; +namespace { class PASETestLoopbackTransport : public Test::LoopbackTransport { @@ -50,19 +50,13 @@ class PASETestLoopbackTransport : public Test::LoopbackTransport 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; + +TestContext sContext; +auto & gLoopback = sContext.GetLoopback(); class TestSecurePairingDelegate : public SessionEstablishmentDelegate { @@ -91,6 +85,8 @@ using namespace System::Clock::Literals; void SecurePairingWaitTest(nlTestSuite * inSuite, void * inContext) { + TestContext & ctx = *reinterpret_cast(inContext); + // Test all combinations of invalid parameters TestSecurePairingDelegate delegate; PASESession pairing; @@ -103,11 +99,16 @@ void SecurePairingWaitTest(nlTestSuite * inSuite, void * inContext) 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 @@ void SecurePairingStartTest(nlTestSuite * inSuite, void * inContext) 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 @@ void SecurePairingStartTest(nlTestSuite * inSuite, void * inContext) 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 @@ void SecurePairingHandshakeTestCommon(nlTestSuite * inSuite, void * inContext, P 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 @@ void SecurePairingHandshakeTestCommon(nlTestSuite * inSuite, void * inContext, P 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 @@ void SecurePairingHandshakeTestCommon(nlTestSuite * inSuite, void * inContext, P 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 @@ void SecurePairingFailedHandshake(nlTestSuite * inSuite, void * inContext) 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 @@ void SecurePairingFailedHandshake(nlTestSuite * inSuite, void * inContext) 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 @@ static nlTestSuite sSuite = }; // clang-format on -static TestContext sContext; - // clang-format on // /** @@ -381,21 +386,15 @@ static TestContext sContext; 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(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_Setup(void * inContext) */ int TestSecurePairing_Teardown(void * inContext) { - CHIP_ERROR err = reinterpret_cast(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