Skip to content

Commit

Permalink
Add initial Messaging Layer unit test to test various negative paths
Browse files Browse the repository at this point in the history
  • Loading branch information
yufengwangca committed Aug 30, 2022
1 parent 9bde9e3 commit c7deee3
Show file tree
Hide file tree
Showing 5 changed files with 294 additions and 0 deletions.
4 changes: 4 additions & 0 deletions src/messaging/tests/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,10 @@ chip_test_suite("tests") {
chip_device_platform != "nrfconnect") {
test_sources += [ "TestExchangeHolder.cpp" ]
}

if (chip_device_platform == "linux") {
test_sources += [ "TestMessagingLayer.cpp" ]
}
}

cflags = [ "-Wconversion" ]
Expand Down
42 changes: 42 additions & 0 deletions src/messaging/tests/MessagingContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
#include <transport/SessionManager.h>
#include <transport/TransportMgr.h>
#include <transport/tests/LoopbackTransportManager.h>
#include <transport/tests/UDPTransportManager.h>

#include <nlunit-test.h>

Expand Down Expand Up @@ -231,6 +232,47 @@ class LoopbackMessagingContext : public LoopbackTransportManager, public Messagi
using LoopbackTransportManager::GetSystemLayer;
};

// UDPMessagingContext enriches MessagingContext with an UDP transport
class UDPMessagingContext : public UDPTransportManager, public MessagingContext
{
public:
virtual ~UDPMessagingContext() {}

/// Initialize the underlying layers.
virtual CHIP_ERROR Init()
{
ReturnErrorOnFailure(chip::Platform::MemoryInit());
ReturnErrorOnFailure(UDPTransportManager::Init());
ReturnErrorOnFailure(MessagingContext::Init(&GetTransportMgr(), &GetIOContext()));
return CHIP_NO_ERROR;
}

// Shutdown all layers, finalize operations
virtual void Shutdown()
{
MessagingContext::Shutdown();
UDPTransportManager::Shutdown();
chip::Platform::MemoryShutdown();
}

// Init/Shutdown Helpers that can be used directly as the nlTestSuite
// initialize/finalize function.
static int Initialize(void * context)
{
auto * ctx = static_cast<UDPMessagingContext *>(context);
return ctx->Init() == CHIP_NO_ERROR ? SUCCESS : FAILURE;
}

static int Finalize(void * context)
{
auto * ctx = static_cast<UDPMessagingContext *>(context);
ctx->Shutdown();
return SUCCESS;
}

using UDPTransportManager::GetSystemLayer;
};

// Class that can be used to capture decrypted message traffic in tests using
// MessagingContext.
class MessageCapturer : public SessionMessageDelegate
Expand Down
180 changes: 180 additions & 0 deletions src/messaging/tests/TestMessagingLayer.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
/*
*
* Copyright (c) 2022 Project CHIP Authors
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

/**
* @file
* This file implements unit tests for the ExchangeManager implementation.
*/

#include <lib/core/CHIPCore.h>
#include <lib/support/CHIPFaultInjection.h>
#include <lib/support/CHIPMem.h>
#include <lib/support/CodeUtils.h>
#include <lib/support/UnitTestContext.h>
#include <lib/support/UnitTestRegistration.h>
#include <messaging/ExchangeContext.h>
#include <messaging/ExchangeMgr.h>
#include <messaging/Flags.h>
#include <messaging/tests/MessagingContext.h>
#include <protocols/Protocols.h>
#include <protocols/echo/Echo.h>
#include <transport/SessionManager.h>
#include <transport/TransportMgr.h>

#include <nlbyteorder.h>
#include <nlunit-test.h>

#include <errno.h>
#include <utility>

namespace {

using namespace chip;
using namespace chip::Inet;
using namespace chip::Transport;
using namespace chip::Messaging;
using namespace chip::Protocols;
using namespace chip::System::Clock::Literals;

using TestContext = Test::UDPMessagingContext;

// The message timeout value in milliseconds.
constexpr System::Clock::Timeout kMessageTimeout = System::Clock::Milliseconds32(100);

class MockAppDelegate : public UnsolicitedMessageHandler, public ExchangeDelegate
{
public:
CHIP_ERROR OnUnsolicitedMessageReceived(const PayloadHeader & payloadHeader, ExchangeDelegate *& newDelegate) override
{
newDelegate = this;
return CHIP_NO_ERROR;
}

CHIP_ERROR OnMessageReceived(ExchangeContext * ec, const PayloadHeader & payloadHeader,
System::PacketBufferHandle && buffer) override
{
IsOnMessageReceivedCalled = true;
return CHIP_NO_ERROR;
}

void OnResponseTimeout(ExchangeContext * ec) override { IsOnResponseTimeoutCalled = true; }

bool IsOnMessageReceivedCalled = false;
bool IsOnResponseTimeoutCalled = false;
};

/**
* Tests sending exchange message with Success:
*
* DUT = sender, PEER = remote device
*
* 1) DUT sends message w/o MRP to PEER
* - Confirm the message is sent successfully
* - Observe DUT response timeout with no response
*/
void CheckExchangeOutgoingMessagesSuccess(nlTestSuite * inSuite, void * inContext)
{
TestContext & ctx = *reinterpret_cast<TestContext *>(inContext);

// create solicited exchange
MockAppDelegate mockSolicitedAppDelegate;
ExchangeContext * ec = ctx.NewExchangeToAlice(&mockSolicitedAppDelegate);

NL_TEST_ASSERT(inSuite, ec != nullptr);
ec->SetResponseTimeout(kMessageTimeout);

CHIP_ERROR err = ec->SendMessage(Echo::MsgType::EchoRequest, System::PacketBufferHandle::New(System::PacketBuffer::kMaxSize),
SendFlags(SendMessageFlags::kExpectResponse).Set(SendMessageFlags::kNoAutoRequestAck));

// Wait for the initial message to fail (should take 330-413ms)
ctx.GetIOContext().DriveIOUntil(500_ms32, [&] { return mockSolicitedAppDelegate.IsOnMessageReceivedCalled; });

NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR);
NL_TEST_ASSERT(inSuite, mockSolicitedAppDelegate.IsOnResponseTimeoutCalled);
}

/**
* Tests sending exchange message with Failure:
*
* DUT = sender, PEER = remote device
*
* 1) DUT configured to drop the outgoing UDP packet
* 2) DUT sends message w/o MRP to PEER
* - Confirm the message is sent with failure
* - Confirm the DUT response timeout timer is cancelled
*/
void CheckExchangeOutgoingMessagesFail(nlTestSuite * inSuite, void * inContext)
{
TestContext & ctx = *reinterpret_cast<TestContext *>(inContext);

// create solicited exchange
MockAppDelegate mockSolicitedAppDelegate;
ExchangeContext * ec = ctx.NewExchangeToAlice(&mockSolicitedAppDelegate);

NL_TEST_ASSERT(inSuite, ec != nullptr);
ec->SetResponseTimeout(kMessageTimeout);

chip::FaultInjection::GetManager().FailAtFault(chip::FaultInjection::kFault_DropOutgoingUDPMsg, 0, 1);

CHIP_ERROR err = ec->SendMessage(Echo::MsgType::EchoRequest, System::PacketBufferHandle::New(System::PacketBuffer::kMaxSize),
SendFlags(SendMessageFlags::kExpectResponse).Set(SendMessageFlags::kNoAutoRequestAck));

// Wait for the initial message to fail (should take 330-413ms)
ctx.GetIOContext().DriveIOUntil(500_ms32, [&] { return mockSolicitedAppDelegate.IsOnMessageReceivedCalled; });

NL_TEST_ASSERT(inSuite, err != CHIP_NO_ERROR);
NL_TEST_ASSERT(inSuite, !mockSolicitedAppDelegate.IsOnResponseTimeoutCalled);
ec->Close();
}

// Test Suite

/**
* Test Suite that lists all the test functions.
*/
// clang-format off
const nlTest sTests[] =
{
NL_TEST_DEF("Test MessagingLayer::ExchangeOutgoingMessagesSuccess", CheckExchangeOutgoingMessagesSuccess),
NL_TEST_DEF("Test MessagingLayer::ExchangeOutgoingMessagesFail", CheckExchangeOutgoingMessagesFail),

NL_TEST_SENTINEL()
};
// clang-format on

// clang-format off
nlTestSuite sSuite =
{
"Test-CHIP-MessagingLayer",
&sTests[0],
TestContext::Initialize,
TestContext::Finalize
};
// clang-format on

} // namespace

/**
* Main
*/
int TestMessagingLayer()
{
return chip::ExecuteTestsWithContext<TestContext>(&sSuite);
}

CHIP_REGISTER_TEST_SUITE(TestMessagingLayer);
6 changes: 6 additions & 0 deletions src/transport/raw/UDP.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
*/
#include <transport/raw/UDP.h>

#include <lib/support/CHIPFaultInjection.h>
#include <lib/support/CodeUtils.h>
#include <lib/support/logging/CHIPLogging.h>
#include <transport/raw/MessageHeader.h>
Expand Down Expand Up @@ -110,6 +111,9 @@ CHIP_ERROR UDP::SendMessage(const Transport::PeerAddress & address, System::Pack
addrInfo.DestPort = address.GetPort();
addrInfo.Interface = address.GetInterface();

// Drop the message and return. Free the buffer.
CHIP_FAULT_INJECT(FaultInjection::kFault_DropOutgoingUDPMsg, msgBuf = nullptr; return CHIP_ERROR_CONNECTION_ABORTED;);

return mUDPEndPoint->SendMsg(&addrInfo, std::move(msgBuf));
}

Expand All @@ -119,6 +123,8 @@ void UDP::OnUdpReceive(Inet::UDPEndPoint * endPoint, System::PacketBufferHandle
UDP * udp = reinterpret_cast<UDP *>(endPoint->mAppState);
PeerAddress peerAddress = PeerAddress::UDP(pktInfo->SrcAddress, pktInfo->SrcPort, pktInfo->Interface);

CHIP_FAULT_INJECT(FaultInjection::kFault_DropIncomingUDPMsg, buffer = nullptr; return;);

udp->HandleMessageReceived(peerAddress, std::move(buffer));

if (err != CHIP_NO_ERROR)
Expand Down
62 changes: 62 additions & 0 deletions src/transport/tests/UDPTransportManager.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/*
* Copyright (c) 2022 Project CHIP Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once

#include <lib/core/CHIPError.h>
#include <lib/support/CodeUtils.h>
#include <platform/CHIPDeviceLayer.h>
#include <transport/TransportMgr.h>
#include <transport/raw/UDP.h>
#include <transport/raw/tests/NetworkTestHelpers.h>

namespace chip {
namespace Test {

class UDPTransportManager
{
public:
/// Initialize the underlying layers.
CHIP_ERROR Init()
{
ReturnErrorOnFailure(mIOContext.Init());
ReturnErrorOnFailure(DeviceLayer::PlatformMgr().InitChipStack());
ReturnErrorOnFailure(mTransportManager.Init(Transport::UdpListenParameters(DeviceLayer::UDPEndPointManager())
.SetAddressType(Inet::IPAddressType::kIPv6)
.SetListenPort(CHIP_PORT + 1)));

return CHIP_NO_ERROR;
}

// Shutdown all layers, finalize operations
void Shutdown()
{
mTransportManager.Close();
DeviceLayer::PlatformMgr().Shutdown();
mIOContext.Shutdown();
}

System::Layer & GetSystemLayer() { return mIOContext.GetSystemLayer(); }
chip::Transport::UDP & GetUDP() { return mTransportManager.GetTransport().template GetImplAtIndex<0>(); }
TransportMgrBase & GetTransportMgr() { return mTransportManager; }
IOContext & GetIOContext() { return mIOContext; }

private:
Test::IOContext mIOContext;
TransportMgr<chip::Transport::UDP> mTransportManager;
};

} // namespace Test
} // namespace chip

0 comments on commit c7deee3

Please sign in to comment.