187 lines
5.7 KiB
C++
187 lines
5.7 KiB
C++
/*
|
|
* 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 <gtest/gtest.h>
|
|
|
|
#include <cstdint>
|
|
#include <iostream>
|
|
#include <thread>
|
|
#include <type_traits>
|
|
|
|
#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<struct ChppTestLinkState *>(linkContext);
|
|
context->fake = new FakeLink();
|
|
context->transportContext = transportContext;
|
|
}
|
|
|
|
static void deinit(void *linkContext) {
|
|
auto context = static_cast<struct ChppTestLinkState *>(linkContext);
|
|
auto *fake = reinterpret_cast<FakeLink *>(context->fake);
|
|
delete fake;
|
|
}
|
|
|
|
static enum ChppLinkErrorCode send(void *linkContext, size_t len) {
|
|
auto context = static_cast<struct ChppTestLinkState *>(linkContext);
|
|
auto *fake = reinterpret_cast<FakeLink *>(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<struct ChppTestLinkState *>(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<FakeLink *>(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<uint8_t> resetPkt = mFakeLink->popTxPacket();
|
|
ASSERT_TRUE(comparePacket(resetPkt, generateResetPacket()))
|
|
<< "Full packet: " << asResetPacket(resetPkt);
|
|
|
|
ChppResetPacket resetAck = generateResetAckPacket();
|
|
chppRxDataCb(&mTransportContext, reinterpret_cast<uint8_t *>(&resetAck),
|
|
sizeof(resetAck));
|
|
|
|
ASSERT_TRUE(mFakeLink->waitForTxPacket());
|
|
std::vector<uint8_t> 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<uint32_t *>(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<uint8_t> 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<uint8_t> 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<uint8_t> pkt = mFakeLink->popTxPacket();
|
|
ChppEmptyPacket ack = generateAck(pkt);
|
|
chppRxDataCb(&mTransportContext, reinterpret_cast<uint8_t *>(&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<uint8_t> pkt = mFakeLink->popTxPacket();
|
|
ChppEmptyPacket ack = generateAck(pkt);
|
|
chppRxDataCb(&mTransportContext, reinterpret_cast<uint8_t *>(&ack),
|
|
sizeof(ack));
|
|
}
|
|
|
|
EXPECT_FALSE(mFakeLink->waitForTxPacket());
|
|
}
|
|
|
|
} // namespace chpp::test
|