/* * Copyright (C) 2022 The Android Open Source Project * * 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 #include #include #include #include #include "chpp/app.h" #include "chpp/crc.h" #include "chpp/link.h" #include "chpp/log.h" #include "chpp/platform/platform_link.h" #include "chpp/transport.h" #include "fake_link.h" #include "packet_util.h" using chpp::test::FakeLink; namespace { static void init(void *linkContext, struct ChppTransportState *transportContext) { auto context = static_cast(linkContext); context->fake = new FakeLink(); context->transportContext = transportContext; } static void deinit(void *linkContext) { auto context = static_cast(linkContext); auto *fake = reinterpret_cast(context->fake); delete fake; } static enum ChppLinkErrorCode send(void *linkContext, size_t len) { auto context = static_cast(linkContext); auto *fake = reinterpret_cast(context->fake); fake->appendTxPacket(&context->txBuffer[0], len); return CHPP_LINK_ERROR_NONE_SENT; } static void doWork(void * /*linkContext*/, uint32_t /*signal*/) {} static void reset(void * /*linkContext*/) {} struct ChppLinkConfiguration getConfig(void * /*linkContext*/) { return ChppLinkConfiguration{ .txBufferLen = CHPP_TEST_LINK_TX_MTU_BYTES, .rxBufferLen = CHPP_TEST_LINK_RX_MTU_BYTES, }; } uint8_t *getTxBuffer(void *linkContext) { auto context = static_cast(linkContext); return &context->txBuffer[0]; } } // namespace const struct ChppLinkApi gLinkApi = { .init = &init, .deinit = &deinit, .send = &send, .doWork = &doWork, .reset = &reset, .getConfig = &getConfig, .getTxBuffer = &getTxBuffer, }; namespace chpp::test { class FakeLinkSyncTests : public testing::Test { protected: void SetUp() override { chppTransportInit(&mTransportContext, &mAppContext, &mLinkContext, &gLinkApi); chppAppInitWithClientServiceSet(&mAppContext, &mTransportContext, /*clientServiceSet=*/{}); mFakeLink = reinterpret_cast(mLinkContext.fake); mWorkThread = std::thread(chppWorkThreadStart, &mTransportContext); // Proceed to the initialized state by performing the CHPP 3-way handshake ASSERT_TRUE(mFakeLink->waitForTxPacket()); std::vector resetPkt = mFakeLink->popTxPacket(); ASSERT_TRUE(comparePacket(resetPkt, generateResetPacket())) << "Full packet: " << asResetPacket(resetPkt); ChppResetPacket resetAck = generateResetAckPacket(); chppRxDataCb(&mTransportContext, reinterpret_cast(&resetAck), sizeof(resetAck)); ASSERT_TRUE(mFakeLink->waitForTxPacket()); std::vector ackPkt = mFakeLink->popTxPacket(); ASSERT_TRUE(comparePacket(ackPkt, generateEmptyPacket())) << "Full packet: " << asChpp(ackPkt); } void TearDown() override { chppWorkThreadStop(&mTransportContext); mWorkThread.join(); EXPECT_EQ(mFakeLink->getTxPacketCount(), 0); } void txPacket() { uint32_t *payload = static_cast(chppMalloc(sizeof(uint32_t))); *payload = 0xdeadbeef; bool enqueued = chppEnqueueTxDatagramOrFail(&mTransportContext, payload, sizeof(uint32_t)); EXPECT_TRUE(enqueued); } ChppTransportState mTransportContext = {}; ChppAppState mAppContext = {}; ChppTestLinkState mLinkContext = {}; FakeLink *mFakeLink; std::thread mWorkThread; }; TEST_F(FakeLinkSyncTests, CheckRetryOnTimeout) { txPacket(); ASSERT_TRUE(mFakeLink->waitForTxPacket()); EXPECT_EQ(mFakeLink->getTxPacketCount(), 1); std::vector pkt1 = mFakeLink->popTxPacket(); // Ideally, to speed up the test, we'd have a mechanism to trigger // chppNotifierWait() to return immediately, to simulate timeout ASSERT_TRUE(mFakeLink->waitForTxPacket()); EXPECT_EQ(mFakeLink->getTxPacketCount(), 1); std::vector pkt2 = mFakeLink->popTxPacket(); // The retry packet should be an exact match of the first one EXPECT_EQ(pkt1, pkt2); } TEST_F(FakeLinkSyncTests, NoRetryAfterAck) { txPacket(); ASSERT_TRUE(mFakeLink->waitForTxPacket()); EXPECT_EQ(mFakeLink->getTxPacketCount(), 1); // Generate and reply back with an ACK std::vector pkt = mFakeLink->popTxPacket(); ChppEmptyPacket ack = generateAck(pkt); chppRxDataCb(&mTransportContext, reinterpret_cast(&ack), sizeof(ack)); // We shouldn't get that packet again EXPECT_FALSE(mFakeLink->waitForTxPacket()); } TEST_F(FakeLinkSyncTests, MultipleNotifications) { constexpr int kNumPackets = 5; for (int i = 0; i < kNumPackets; i++) { txPacket(); } for (int i = 0; i < kNumPackets; i++) { ASSERT_TRUE(mFakeLink->waitForTxPacket()); // Generate and reply back with an ACK std::vector pkt = mFakeLink->popTxPacket(); ChppEmptyPacket ack = generateAck(pkt); chppRxDataCb(&mTransportContext, reinterpret_cast(&ack), sizeof(ack)); } EXPECT_FALSE(mFakeLink->waitForTxPacket()); } } // namespace chpp::test