blob: 69024d13af2122d24676df5e865c686ccf2ec6e7 [file] [log] [blame]
/*
*
* Copyright (c) 2025 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.
*/
#include <cstdint>
#include <cstring>
#include <string>
#include <type_traits>
#include <utility>
#include <pw_unit_test/framework.h>
#include <lib/core/CHIPError.h>
#include <lib/core/StringBuilderAdapters.h>
#include <lib/support/CHIPMem.h>
#include <lib/support/Span.h>
#include <lib/support/TypeTraits.h>
#include <lib/support/logging/CHIPLogging.h>
#include <platform/CHIPDeviceLayer.h>
#include <system/SystemLayer.h>
#include <system/SystemPacketBuffer.h>
#include <wifipaf/WiFiPAFError.h>
#include <wifipaf/WiFiPAFLayer.h>
#include <wifipaf/WiFiPAFLayerDelegate.h>
namespace chip {
namespace WiFiPAF {
class TestWiFiPAFLayer : public WiFiPAFLayer, private WiFiPAFLayerDelegate, public ::testing::Test
{
public:
static void SetUpTestSuite()
{
ASSERT_EQ(chip::Platform::MemoryInit(), CHIP_NO_ERROR);
ASSERT_EQ(DeviceLayer::SystemLayer().Init(), CHIP_NO_ERROR);
}
static void TearDownTestSuite()
{
DeviceLayer::SystemLayer().Shutdown();
chip::Platform::MemoryShutdown();
}
void SetUp() override
{
ASSERT_EQ(Init(&DeviceLayer::SystemLayer()), CHIP_NO_ERROR);
mWiFiPAFTransport = this;
InitialPafInfo();
}
void TearDown() override
{
mWiFiPAFTransport = nullptr;
Shutdown([](uint32_t id, WiFiPAF::WiFiPafRole role) {});
}
CHIP_ERROR WiFiPAFMessageReceived(WiFiPAFSession & RxInfo, System::PacketBufferHandle && msg) override { return CHIP_NO_ERROR; }
CHIP_ERROR WiFiPAFMessageSend(WiFiPAFSession & TxInfo, System::PacketBufferHandle && msg) override { return CHIP_NO_ERROR; }
CHIP_ERROR WiFiPAFCloseSession(WiFiPAFSession & SessionInfo) override { return CHIP_NO_ERROR; }
bool WiFiPAFResourceAvailable() override { return mResourceAvailable; }
static constexpr size_t kTestPacketLength = 100;
static constexpr size_t kTestPacketLengthLong = 500;
void SetEndPoint(WiFiPAFEndPoint * pEndPoint) { mEndPoint = pEndPoint; }
void EpDoClose(uint8_t flags, CHIP_ERROR err) { return mEndPoint->DoClose(flags, err); }
CHIP_ERROR EpDriveStandAloneAck() { return mEndPoint->DriveStandAloneAck(); }
CHIP_ERROR EpDoSendStandAloneAck() { return mEndPoint->DoSendStandAloneAck(); }
void EpSetRxNextSeqNum(SequenceNumber_t seq) { mEndPoint->mPafTP.mRxNextSeqNum = seq; }
WiFiPAFTP::State_t EpGetTxState() { return mEndPoint->mPafTP.mTxState; }
bool mResourceAvailable = true;
bool isSendQueueNull() { return mEndPoint->mSendQueue.IsNull(); }
uint8_t GetResourceWaitCount() { return mEndPoint->mResourceWaitCount; }
private:
WiFiPAFEndPoint * mEndPoint;
};
TEST_F(TestWiFiPAFLayer, CheckWiFiPAFTransportCapabilitiesRequestMessage)
{
auto buf = System::PacketBufferHandle::New(100);
ASSERT_FALSE(buf.IsNull());
PAFTransportCapabilitiesRequestMessage msg{};
msg.SetSupportedProtocolVersion(0, CHIP_PAF_TRANSPORT_PROTOCOL_MIN_SUPPORTED_VERSION);
msg.mMtu = CHIP_PAF_DEFAULT_MTU;
msg.mWindowSize = PAF_MAX_RECEIVE_WINDOW_SIZE;
ASSERT_EQ(msg.Encode(buf), CHIP_NO_ERROR);
ChipLogByteSpan(Test, ByteSpan(buf->Start(), buf->DataLength()));
PAFTransportCapabilitiesRequestMessage msgVerify;
ASSERT_EQ(PAFTransportCapabilitiesRequestMessage::Decode(buf, msgVerify), CHIP_NO_ERROR);
EXPECT_EQ(memcmp(msg.mSupportedProtocolVersions, msgVerify.mSupportedProtocolVersions, sizeof(msg.mSupportedProtocolVersions)),
0);
EXPECT_EQ(msg.mMtu, msgVerify.mMtu);
EXPECT_EQ(msg.mWindowSize, msgVerify.mWindowSize);
}
TEST_F(TestWiFiPAFLayer, CheckWiFiPAFTransportCapabilitiesResponseMessage)
{
auto buf = System::PacketBufferHandle::New(100);
ASSERT_FALSE(buf.IsNull());
PAFTransportCapabilitiesResponseMessage msg{};
msg.mSelectedProtocolVersion = CHIP_PAF_TRANSPORT_PROTOCOL_MAX_SUPPORTED_VERSION;
msg.mFragmentSize = CHIP_PAF_DEFAULT_MTU;
msg.mWindowSize = PAF_MAX_RECEIVE_WINDOW_SIZE;
EXPECT_EQ(msg.Encode(buf), CHIP_NO_ERROR);
ChipLogByteSpan(Test, ByteSpan(buf->Start(), buf->DataLength()));
PAFTransportCapabilitiesResponseMessage msgVerify;
ASSERT_EQ(PAFTransportCapabilitiesResponseMessage::Decode(buf, msgVerify), CHIP_NO_ERROR);
EXPECT_EQ(msg.mSelectedProtocolVersion, msgVerify.mSelectedProtocolVersion);
EXPECT_EQ(msg.mFragmentSize, msgVerify.mFragmentSize);
EXPECT_EQ(msg.mWindowSize, msgVerify.mWindowSize);
}
TEST_F(TestWiFiPAFLayer, CheckPafSession)
{
// Add the 1st session by giving node_id, discriminator
WiFiPAF::WiFiPAFSession sessionInfo = { .role = kWiFiPafRole_Subscriber, .nodeId = 0x1, .discriminator = 0xF01 };
EXPECT_EQ(AddPafSession(PafInfoAccess::kAccNodeInfo, sessionInfo), CHIP_NO_ERROR);
// Add the 2nd session
sessionInfo = { .role = WiFiPAF::WiFiPafRole::kWiFiPafRole_Subscriber, .nodeId = 0x2, .discriminator = 0xF02 };
EXPECT_EQ(AddPafSession(PafInfoAccess::kAccNodeInfo, sessionInfo), CHIP_NO_ERROR);
// Add the 3rd session => expect: no space
sessionInfo.id = 0x3;
EXPECT_EQ(AddPafSession(PafInfoAccess::kAccSessionId, sessionInfo), CHIP_ERROR_PROVIDER_LIST_EXHAUSTED);
// Get the session info by giving node_id
sessionInfo.nodeId = 0x1;
auto pPafSessionInfo_nodeid = GetPAFInfo(PafInfoAccess::kAccNodeId, sessionInfo);
pPafSessionInfo_nodeid->id = 0x1;
EXPECT_EQ(pPafSessionInfo_nodeid->nodeId, 0x1u);
EXPECT_EQ(pPafSessionInfo_nodeid->discriminator, 0xF01);
// Get the session info by giving the discriminator
sessionInfo.discriminator = 0xF01;
auto pPafSessionInfo_disc = GetPAFInfo(PafInfoAccess::kAccDisc, sessionInfo);
EXPECT_NE(pPafSessionInfo_disc, nullptr);
EXPECT_EQ(pPafSessionInfo_disc->nodeId, 0x1u);
pPafSessionInfo_disc->id = 1;
sessionInfo.discriminator = 0xF02;
pPafSessionInfo_disc = GetPAFInfo(PafInfoAccess::kAccDisc, sessionInfo);
EXPECT_NE(pPafSessionInfo_disc, nullptr);
EXPECT_EQ(pPafSessionInfo_disc->nodeId, 0x2u);
EXPECT_EQ(pPafSessionInfo_disc->discriminator, 0xF02);
pPafSessionInfo_disc->id = 2;
// Get the session info by giving the session id
sessionInfo.id = 0x1;
auto pPafSessionInfo_id = GetPAFInfo(PafInfoAccess::kAccSessionId, sessionInfo);
EXPECT_NE(pPafSessionInfo_id, nullptr);
EXPECT_EQ(pPafSessionInfo_id->nodeId, 0x1u);
EXPECT_EQ(pPafSessionInfo_id->discriminator, 0xF01);
// Remove the session
sessionInfo.id = 0x1;
EXPECT_EQ(RmPafSession(PafInfoAccess::kAccSessionId, sessionInfo), CHIP_NO_ERROR);
sessionInfo.id = 0x2;
EXPECT_EQ(RmPafSession(PafInfoAccess::kAccSessionId, sessionInfo), CHIP_NO_ERROR);
EXPECT_EQ(RmPafSession(PafInfoAccess::kAccNodeInfo, sessionInfo), CHIP_ERROR_NOT_IMPLEMENTED);
EXPECT_EQ(RmPafSession(PafInfoAccess::kAccSessionId, sessionInfo), CHIP_ERROR_NOT_FOUND);
}
TEST_F(TestWiFiPAFLayer, CheckRunAsCommissioner)
{
WiFiPAFSession sessionInfo = {
.role = kWiFiPafRole_Subscriber,
.id = 1,
.peer_id = 1,
.peer_addr = { 0xd0, 0x17, 0x69, 0xee, 0x7f, 0x3c },
.nodeId = 1,
.discriminator = 0xF00,
};
WiFiPAFEndPoint * newEndPoint = nullptr;
EXPECT_EQ(NewEndPoint(&newEndPoint, sessionInfo, sessionInfo.role), CHIP_NO_ERROR);
EXPECT_NE(newEndPoint, nullptr);
SetEndPoint(newEndPoint);
newEndPoint->mState = WiFiPAFEndPoint::kState_Ready;
SetWiFiPAFState(State::kInitialized);
EXPECT_EQ(GetWiFiPAFState(), State::kInitialized);
EXPECT_EQ(newEndPoint->StartConnect(), CHIP_NO_ERROR);
EXPECT_EQ(AddPafSession(PafInfoAccess::kAccSessionId, sessionInfo), CHIP_NO_ERROR);
newEndPoint->mState = WiFiPAFEndPoint::kState_Connected;
// Send the capability request packet
constexpr uint8_t bufCapReq[] = { 0x65, 0x6c, 0x04, 0x00, 0x00, 0x00, 0x5e, 0x01, 0x06 };
auto packetCapReq = System::PacketBufferHandle::NewWithData(bufCapReq, sizeof(bufCapReq));
EXPECT_EQ(SendMessage(sessionInfo, std::move(packetCapReq)), CHIP_NO_ERROR);
EXPECT_EQ(HandleWriteConfirmed(sessionInfo, true), CHIP_NO_ERROR);
// Receive the capability response packet
constexpr uint8_t bufCapResp[] = { 0x65, 0x6c, 0x04, 0x5b, 0x01, 0x06 };
auto packetCapResp = System::PacketBufferHandle::NewWithData(bufCapResp, sizeof(bufCapResp));
newEndPoint->mState = WiFiPAFEndPoint::kState_Connecting;
EXPECT_EQ(OnWiFiPAFMessageReceived(sessionInfo, std::move(packetCapResp)), true);
// Send a packet
auto buf = System::PacketBufferHandle::New(kTestPacketLength);
buf->SetDataLength(kTestPacketLength);
memset(buf->Start(), 0, buf->DataLength());
EXPECT_EQ(SendMessage(sessionInfo, std::move(buf)), CHIP_NO_ERROR);
EXPECT_EQ(HandleWriteConfirmed(sessionInfo, true), CHIP_NO_ERROR);
constexpr uint8_t buf_rx[] = {
to_underlying(WiFiPAFTP::HeaderFlags::kStartMessage) | to_underlying(WiFiPAFTP::HeaderFlags::kEndMessage) |
to_underlying(WiFiPAFTP::HeaderFlags::kFragmentAck),
0x01,
0x01,
0x00,
0x00, // payload
};
// Receive a pcaket
auto packet_rx = System::PacketBufferHandle::NewWithData(buf_rx, sizeof(buf_rx));
EXPECT_EQ(packet_rx->DataLength(), static_cast<size_t>(5));
EXPECT_EQ(newEndPoint->Receive(std::move(packet_rx)), CHIP_NO_ERROR);
EXPECT_EQ(EpDriveStandAloneAck(), CHIP_NO_ERROR);
EXPECT_EQ(EpDoSendStandAloneAck(), CHIP_NO_ERROR);
// Close the session
EXPECT_EQ(RmPafSession(PafInfoAccess::kAccSessionId, sessionInfo), CHIP_NO_ERROR);
EpDoClose(kWiFiPAFCloseFlag_AbortTransmission, WIFIPAF_ERROR_APP_CLOSED_CONNECTION);
}
TEST_F(TestWiFiPAFLayer, CheckRunAsCommissionee)
{
WiFiPAFSession sessionInfo = {
.role = kWiFiPafRole_Publisher,
.id = 1,
.peer_id = 1,
.peer_addr = { 0xd0, 0x17, 0x69, 0xee, 0x7f, 0x3c },
.nodeId = 1,
.discriminator = 0xF00,
};
WiFiPAFEndPoint * newEndPoint = nullptr;
EXPECT_EQ(NewEndPoint(&newEndPoint, sessionInfo, sessionInfo.role), CHIP_NO_ERROR);
EXPECT_NE(newEndPoint, nullptr);
SetEndPoint(newEndPoint);
EXPECT_EQ(AddPafSession(PafInfoAccess::kAccSessionId, sessionInfo), CHIP_NO_ERROR);
newEndPoint->mState = WiFiPAFEndPoint::kState_Ready;
// Receive the Capability_Request packet
constexpr uint8_t bufCapReq[] = { 0x65, 0x6c, 0x04, 0x00, 0x00, 0x00, 0x5e, 0x01, 0x06 };
auto packetCapReq = System::PacketBufferHandle::NewWithData(bufCapReq, sizeof(bufCapReq));
EXPECT_EQ(OnWiFiPAFMessageReceived(sessionInfo, std::move(packetCapReq)), true);
// Reply the Capability Response packet
constexpr uint8_t bufCapResp[] = { 0x65, 0x6c, 0x04, 0x5b, 0x01, 0x06 };
auto packetCapResp = System::PacketBufferHandle::NewWithData(bufCapResp, sizeof(bufCapResp));
EXPECT_EQ(HandleWriteConfirmed(sessionInfo, true), CHIP_NO_ERROR);
EXPECT_EQ(SendMessage(sessionInfo, std::move(packetCapResp)), CHIP_NO_ERROR);
EXPECT_EQ(HandleWriteConfirmed(sessionInfo, true), CHIP_NO_ERROR);
// Send a long packet
auto buf = System::PacketBufferHandle::New(kTestPacketLengthLong);
buf->SetDataLength(kTestPacketLengthLong);
memset(buf->Start(), 0, buf->DataLength());
EXPECT_EQ(SendMessage(sessionInfo, std::move(buf)), CHIP_NO_ERROR);
EXPECT_EQ(EpGetTxState(), WiFiPAFTP::kState_InProgress);
EXPECT_EQ(HandleWriteConfirmed(sessionInfo, true), CHIP_NO_ERROR);
EXPECT_EQ(EpGetTxState(), WiFiPAFTP::kState_Complete);
EXPECT_EQ(HandleWriteConfirmed(sessionInfo, true), CHIP_NO_ERROR);
// Send a normal packet
buf = System::PacketBufferHandle::New(kTestPacketLength);
buf->SetDataLength(kTestPacketLength);
memset(buf->Start(), 0, buf->DataLength());
EXPECT_EQ(SendMessage(sessionInfo, std::move(buf)), CHIP_NO_ERROR);
EXPECT_EQ(EpGetTxState(), WiFiPAFTP::kState_Complete);
EXPECT_EQ(HandleWriteConfirmed(sessionInfo, true), CHIP_NO_ERROR);
// Receive a packet, sn#1
constexpr uint8_t buf_rx[] = {
to_underlying(WiFiPAFTP::HeaderFlags::kStartMessage) | to_underlying(WiFiPAFTP::HeaderFlags::kEndMessage) |
to_underlying(WiFiPAFTP::HeaderFlags::kFragmentAck),
0x01,
0x01, // sn
0x00,
0x00, // payload
};
auto packet_rx = System::PacketBufferHandle::NewWithData(buf_rx, sizeof(buf_rx));
EXPECT_EQ(packet_rx->DataLength(), static_cast<size_t>(5));
EpSetRxNextSeqNum(1);
EXPECT_EQ(newEndPoint->Receive(std::move(packet_rx)), CHIP_NO_ERROR);
// Receive the duplicate packet
packet_rx = System::PacketBufferHandle::NewWithData(buf_rx, sizeof(buf_rx));
EXPECT_EQ(packet_rx->DataLength(), static_cast<size_t>(5));
EXPECT_EQ(newEndPoint->Receive(std::move(packet_rx)), CHIP_NO_ERROR);
// Test Reordering
// Receive pkt sn#3
constexpr uint8_t buf_rx_sn3[] = {
to_underlying(WiFiPAFTP::HeaderFlags::kStartMessage) | to_underlying(WiFiPAFTP::HeaderFlags::kEndMessage) |
to_underlying(WiFiPAFTP::HeaderFlags::kFragmentAck),
0x1,
0x03, // sn
0x00,
0x00, // payload
};
packet_rx = System::PacketBufferHandle::NewWithData(buf_rx_sn3, sizeof(buf_rx_sn3));
EXPECT_EQ(packet_rx->DataLength(), static_cast<size_t>(5));
EXPECT_EQ(newEndPoint->Receive(std::move(packet_rx)), CHIP_NO_ERROR);
// Receive pkt sn#2
constexpr uint8_t buf_rx_sn2[] = {
to_underlying(WiFiPAFTP::HeaderFlags::kStartMessage) | to_underlying(WiFiPAFTP::HeaderFlags::kEndMessage) |
to_underlying(WiFiPAFTP::HeaderFlags::kFragmentAck),
0x01,
0x02, // sn
0x00,
0x00, // payload
};
packet_rx = System::PacketBufferHandle::NewWithData(buf_rx_sn2, sizeof(buf_rx_sn2));
EXPECT_EQ(packet_rx->DataLength(), static_cast<size_t>(5));
EXPECT_EQ(newEndPoint->Receive(std::move(packet_rx)), CHIP_NO_ERROR);
// Test, send chained packet
constexpr uint8_t buf_chain[] = {
to_underlying(WiFiPAFTP::HeaderFlags::kStartMessage) | to_underlying(WiFiPAFTP::HeaderFlags::kEndMessage),
0x01,
0x01,
0x00,
0x00, // payload
};
auto packet_c1 = System::PacketBufferHandle::NewWithData(buf_chain, sizeof(buf_chain));
auto packet_c2 = System::PacketBufferHandle::NewWithData(buf_chain, sizeof(buf_chain));
packet_c1->AddToEnd(std::move(packet_c2));
EXPECT_EQ(packet_c1->HasChainedBuffer(), true);
EXPECT_EQ(newEndPoint->Send(std::move(packet_c1)), CHIP_NO_ERROR);
EXPECT_EQ(HandleWriteConfirmed(sessionInfo, true), CHIP_NO_ERROR);
// Test, Send the packet while resource is unavaialbe -> available
mResourceAvailable = false;
auto packet_resource = System::PacketBufferHandle::NewWithData(buf_chain, sizeof(buf_chain));
EXPECT_EQ(newEndPoint->Send(std::move(packet_resource)), CHIP_NO_ERROR);
// break because resource is unavailable
EXPECT_EQ(isSendQueueNull(), false);
EXPECT_GT(GetResourceWaitCount(), 0);
// Resource is available now
mResourceAvailable = true;
// PAF packets shoudl be sent within a second
System::Clock::Internal::MockClock clock;
System::Clock::ClockBase * realClock = &System::SystemClock();
constexpr System::Clock::Seconds64 pauseSec = System::Clock::Seconds64(2);
clock.SetMonotonic(pauseSec);
System::Clock::Internal::SetSystemClockForTesting(&clock);
EXPECT_EQ(HandleWriteConfirmed(sessionInfo, true), CHIP_NO_ERROR);
// PAF packet has been sent
EXPECT_EQ(isSendQueueNull(), true);
EXPECT_EQ(GetResourceWaitCount(), 0);
// Close the session
EXPECT_EQ(RmPafSession(PafInfoAccess::kAccSessionId, sessionInfo), CHIP_NO_ERROR);
EpDoClose(kWiFiPAFCloseFlag_AbortTransmission, WIFIPAF_ERROR_APP_CLOSED_CONNECTION);
System::Clock::Internal::SetSystemClockForTesting(realClock);
}
}; // namespace WiFiPAF
}; // namespace chip