blob: f926dd6b042bc41a18c99df4856d6c860da35a51 [file] [log] [blame]
/*
* 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 <string.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 {
memset(&mLinkContext, 0, sizeof(mLinkContext));
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
CHPP_LOGI("Send a RESET packet");
ASSERT_TRUE(mFakeLink->waitForTxPacket());
std::vector<uint8_t> resetPkt = mFakeLink->popTxPacket();
ASSERT_TRUE(comparePacket(resetPkt, generateResetPacket()))
<< "Full packet: " << asResetPacket(resetPkt);
CHPP_LOGI("Receive a RESET ACK packet");
ChppResetPacket resetAck = generateResetAckPacket();
chppRxDataCb(&mTransportContext, reinterpret_cast<uint8_t *>(&resetAck),
sizeof(resetAck));
// chppProcessResetAck() results in sending a no error packet.
CHPP_LOGI("Send CHPP_TRANSPORT_ERROR_NONE packet");
ASSERT_TRUE(mFakeLink->waitForTxPacket());
std::vector<uint8_t> ackPkt = mFakeLink->popTxPacket();
ASSERT_TRUE(comparePacket(ackPkt, generateEmptyPacket()))
<< "Full packet: " << asChpp(ackPkt);
CHPP_LOGI("CHPP handshake complete");
}
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();
// Not calling chppRxDataCb() will result in a timeout.
// 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