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