1 /*
2  * Copyright (C) 2022 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include <gtest/gtest.h>
18 
19 #include <string.h>
20 #include <cstdint>
21 #include <iostream>
22 #include <thread>
23 #include <type_traits>
24 
25 #include "chpp/app.h"
26 #include "chpp/crc.h"
27 #include "chpp/link.h"
28 #include "chpp/log.h"
29 #include "chpp/platform/platform_link.h"
30 #include "chpp/transport.h"
31 #include "fake_link.h"
32 #include "packet_util.h"
33 
34 using chpp::test::FakeLink;
35 
36 namespace {
37 
init(void * linkContext,struct ChppTransportState * transportContext)38 static void init(void *linkContext,
39                  struct ChppTransportState *transportContext) {
40   auto context = static_cast<struct ChppTestLinkState *>(linkContext);
41   context->fake = new FakeLink();
42   context->transportContext = transportContext;
43 }
44 
deinit(void * linkContext)45 static void deinit(void *linkContext) {
46   auto context = static_cast<struct ChppTestLinkState *>(linkContext);
47   auto *fake = reinterpret_cast<FakeLink *>(context->fake);
48   delete fake;
49 }
50 
send(void * linkContext,size_t len)51 static enum ChppLinkErrorCode send(void *linkContext, size_t len) {
52   auto context = static_cast<struct ChppTestLinkState *>(linkContext);
53   auto *fake = reinterpret_cast<FakeLink *>(context->fake);
54   fake->appendTxPacket(&context->txBuffer[0], len);
55   return CHPP_LINK_ERROR_NONE_SENT;
56 }
57 
doWork(void *,uint32_t)58 static void doWork(void * /*linkContext*/, uint32_t /*signal*/) {}
59 
reset(void *)60 static void reset(void * /*linkContext*/) {}
61 
getConfig(void *)62 struct ChppLinkConfiguration getConfig(void * /*linkContext*/) {
63   return ChppLinkConfiguration{
64       .txBufferLen = CHPP_TEST_LINK_TX_MTU_BYTES,
65       .rxBufferLen = CHPP_TEST_LINK_RX_MTU_BYTES,
66   };
67 }
68 
getTxBuffer(void * linkContext)69 uint8_t *getTxBuffer(void *linkContext) {
70   auto context = static_cast<struct ChppTestLinkState *>(linkContext);
71   return &context->txBuffer[0];
72 }
73 
74 }  // namespace
75 
76 const struct ChppLinkApi gLinkApi = {
77     .init = &init,
78     .deinit = &deinit,
79     .send = &send,
80     .doWork = &doWork,
81     .reset = &reset,
82     .getConfig = &getConfig,
83     .getTxBuffer = &getTxBuffer,
84 };
85 
86 namespace chpp::test {
87 
88 class FakeLinkSyncTests : public testing::Test {
89  protected:
SetUp()90   void SetUp() override {
91     memset(&mLinkContext, 0, sizeof(mLinkContext));
92     chppTransportInit(&mTransportContext, &mAppContext, &mLinkContext,
93                       &gLinkApi);
94     chppAppInitWithClientServiceSet(&mAppContext, &mTransportContext,
95                                     /*clientServiceSet=*/{});
96     mFakeLink = reinterpret_cast<FakeLink *>(mLinkContext.fake);
97 
98     mWorkThread = std::thread(chppWorkThreadStart, &mTransportContext);
99 
100     // Proceed to the initialized state by performing the CHPP 3-way handshake
101     CHPP_LOGI("Send a RESET packet");
102     ASSERT_TRUE(mFakeLink->waitForTxPacket());
103     std::vector<uint8_t> resetPkt = mFakeLink->popTxPacket();
104     ASSERT_TRUE(comparePacket(resetPkt, generateResetPacket()))
105         << "Full packet: " << asResetPacket(resetPkt);
106 
107     CHPP_LOGI("Receive a RESET ACK packet");
108     ChppResetPacket resetAck = generateResetAckPacket();
109     chppRxDataCb(&mTransportContext, reinterpret_cast<uint8_t *>(&resetAck),
110                  sizeof(resetAck));
111 
112     // chppProcessResetAck() results in sending a no error packet.
113     CHPP_LOGI("Send CHPP_TRANSPORT_ERROR_NONE packet");
114     ASSERT_TRUE(mFakeLink->waitForTxPacket());
115     std::vector<uint8_t> ackPkt = mFakeLink->popTxPacket();
116     ASSERT_TRUE(comparePacket(ackPkt, generateEmptyPacket()))
117         << "Full packet: " << asChpp(ackPkt);
118     CHPP_LOGI("CHPP handshake complete");
119   }
120 
TearDown()121   void TearDown() override {
122     chppWorkThreadStop(&mTransportContext);
123     mWorkThread.join();
124     EXPECT_EQ(mFakeLink->getTxPacketCount(), 0);
125   }
126 
txPacket()127   void txPacket() {
128     uint32_t *payload = static_cast<uint32_t *>(chppMalloc(sizeof(uint32_t)));
129     *payload = 0xdeadbeef;
130     bool enqueued = chppEnqueueTxDatagramOrFail(&mTransportContext, payload,
131                                                 sizeof(uint32_t));
132     EXPECT_TRUE(enqueued);
133   }
134 
135   ChppTransportState mTransportContext = {};
136   ChppAppState mAppContext = {};
137   ChppTestLinkState mLinkContext;
138   FakeLink *mFakeLink;
139   std::thread mWorkThread;
140 };
141 
TEST_F(FakeLinkSyncTests,CheckRetryOnTimeout)142 TEST_F(FakeLinkSyncTests, CheckRetryOnTimeout) {
143   txPacket();
144   ASSERT_TRUE(mFakeLink->waitForTxPacket());
145   EXPECT_EQ(mFakeLink->getTxPacketCount(), 1);
146 
147   std::vector<uint8_t> pkt1 = mFakeLink->popTxPacket();
148 
149   // Not calling chppRxDataCb() will result in a timeout.
150   // Ideally, to speed up the test, we'd have a mechanism to trigger
151   // chppNotifierWait() to return immediately, to simulate timeout
152   ASSERT_TRUE(mFakeLink->waitForTxPacket());
153   EXPECT_EQ(mFakeLink->getTxPacketCount(), 1);
154   std::vector<uint8_t> pkt2 = mFakeLink->popTxPacket();
155 
156   // The retry packet should be an exact match of the first one
157   EXPECT_EQ(pkt1, pkt2);
158 }
159 
TEST_F(FakeLinkSyncTests,NoRetryAfterAck)160 TEST_F(FakeLinkSyncTests, NoRetryAfterAck) {
161   txPacket();
162   ASSERT_TRUE(mFakeLink->waitForTxPacket());
163   EXPECT_EQ(mFakeLink->getTxPacketCount(), 1);
164 
165   // Generate and reply back with an ACK
166   std::vector<uint8_t> pkt = mFakeLink->popTxPacket();
167   ChppEmptyPacket ack = generateAck(pkt);
168   chppRxDataCb(&mTransportContext, reinterpret_cast<uint8_t *>(&ack),
169                sizeof(ack));
170 
171   // We shouldn't get that packet again
172   EXPECT_FALSE(mFakeLink->waitForTxPacket());
173 }
174 
TEST_F(FakeLinkSyncTests,MultipleNotifications)175 TEST_F(FakeLinkSyncTests, MultipleNotifications) {
176   constexpr int kNumPackets = 5;
177   for (int i = 0; i < kNumPackets; i++) {
178     txPacket();
179   }
180 
181   for (int i = 0; i < kNumPackets; i++) {
182     ASSERT_TRUE(mFakeLink->waitForTxPacket());
183 
184     // Generate and reply back with an ACK
185     std::vector<uint8_t> pkt = mFakeLink->popTxPacket();
186     ChppEmptyPacket ack = generateAck(pkt);
187     chppRxDataCb(&mTransportContext, reinterpret_cast<uint8_t *>(&ack),
188                  sizeof(ack));
189   }
190 
191   EXPECT_FALSE(mFakeLink->waitForTxPacket());
192 }
193 
194 }  // namespace chpp::test
195