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 <cstdint>
20 #include <iostream>
21 #include <thread>
22 #include <type_traits>
23
24 #include "chpp/app.h"
25 #include "chpp/crc.h"
26 #include "chpp/link.h"
27 #include "chpp/log.h"
28 #include "chpp/platform/platform_link.h"
29 #include "chpp/transport.h"
30 #include "fake_link.h"
31 #include "packet_util.h"
32
33 using chpp::test::FakeLink;
34
35 namespace {
36
init(void * linkContext,struct ChppTransportState * transportContext)37 static void init(void *linkContext,
38 struct ChppTransportState *transportContext) {
39 auto context = static_cast<struct ChppTestLinkState *>(linkContext);
40 context->fake = new FakeLink();
41 context->transportContext = transportContext;
42 }
43
deinit(void * linkContext)44 static void deinit(void *linkContext) {
45 auto context = static_cast<struct ChppTestLinkState *>(linkContext);
46 auto *fake = reinterpret_cast<FakeLink *>(context->fake);
47 delete fake;
48 }
49
send(void * linkContext,size_t len)50 static enum ChppLinkErrorCode send(void *linkContext, size_t len) {
51 auto context = static_cast<struct ChppTestLinkState *>(linkContext);
52 auto *fake = reinterpret_cast<FakeLink *>(context->fake);
53 fake->appendTxPacket(&context->txBuffer[0], len);
54 return CHPP_LINK_ERROR_NONE_SENT;
55 }
56
doWork(void *,uint32_t)57 static void doWork(void * /*linkContext*/, uint32_t /*signal*/) {}
58
reset(void *)59 static void reset(void * /*linkContext*/) {}
60
getConfig(void *)61 struct ChppLinkConfiguration getConfig(void * /*linkContext*/) {
62 return ChppLinkConfiguration{
63 .txBufferLen = CHPP_TEST_LINK_TX_MTU_BYTES,
64 .rxBufferLen = CHPP_TEST_LINK_RX_MTU_BYTES,
65 };
66 }
67
getTxBuffer(void * linkContext)68 uint8_t *getTxBuffer(void *linkContext) {
69 auto context = static_cast<struct ChppTestLinkState *>(linkContext);
70 return &context->txBuffer[0];
71 }
72
73 } // namespace
74
75 const struct ChppLinkApi gLinkApi = {
76 .init = &init,
77 .deinit = &deinit,
78 .send = &send,
79 .doWork = &doWork,
80 .reset = &reset,
81 .getConfig = &getConfig,
82 .getTxBuffer = &getTxBuffer,
83 };
84
85 namespace chpp::test {
86
87 class FakeLinkSyncTests : public testing::Test {
88 protected:
SetUp()89 void SetUp() override {
90 chppTransportInit(&mTransportContext, &mAppContext, &mLinkContext,
91 &gLinkApi);
92 chppAppInitWithClientServiceSet(&mAppContext, &mTransportContext,
93 /*clientServiceSet=*/{});
94 mFakeLink = reinterpret_cast<FakeLink *>(mLinkContext.fake);
95
96 mWorkThread = std::thread(chppWorkThreadStart, &mTransportContext);
97
98 // Proceed to the initialized state by performing the CHPP 3-way handshake
99 ASSERT_TRUE(mFakeLink->waitForTxPacket());
100 std::vector<uint8_t> resetPkt = mFakeLink->popTxPacket();
101 ASSERT_TRUE(comparePacket(resetPkt, generateResetPacket()))
102 << "Full packet: " << asResetPacket(resetPkt);
103
104 ChppResetPacket resetAck = generateResetAckPacket();
105 chppRxDataCb(&mTransportContext, reinterpret_cast<uint8_t *>(&resetAck),
106 sizeof(resetAck));
107
108 ASSERT_TRUE(mFakeLink->waitForTxPacket());
109 std::vector<uint8_t> ackPkt = mFakeLink->popTxPacket();
110 ASSERT_TRUE(comparePacket(ackPkt, generateEmptyPacket()))
111 << "Full packet: " << asChpp(ackPkt);
112 }
113
TearDown()114 void TearDown() override {
115 chppWorkThreadStop(&mTransportContext);
116 mWorkThread.join();
117 EXPECT_EQ(mFakeLink->getTxPacketCount(), 0);
118 }
119
txPacket()120 void txPacket() {
121 uint32_t *payload = static_cast<uint32_t *>(chppMalloc(sizeof(uint32_t)));
122 *payload = 0xdeadbeef;
123 bool enqueued = chppEnqueueTxDatagramOrFail(&mTransportContext, payload,
124 sizeof(uint32_t));
125 EXPECT_TRUE(enqueued);
126 }
127
128 ChppTransportState mTransportContext = {};
129 ChppAppState mAppContext = {};
130 ChppTestLinkState mLinkContext = {};
131 FakeLink *mFakeLink;
132 std::thread mWorkThread;
133 };
134
TEST_F(FakeLinkSyncTests,CheckRetryOnTimeout)135 TEST_F(FakeLinkSyncTests, CheckRetryOnTimeout) {
136 txPacket();
137 ASSERT_TRUE(mFakeLink->waitForTxPacket());
138 EXPECT_EQ(mFakeLink->getTxPacketCount(), 1);
139
140 std::vector<uint8_t> pkt1 = mFakeLink->popTxPacket();
141
142 // Ideally, to speed up the test, we'd have a mechanism to trigger
143 // chppNotifierWait() to return immediately, to simulate timeout
144 ASSERT_TRUE(mFakeLink->waitForTxPacket());
145 EXPECT_EQ(mFakeLink->getTxPacketCount(), 1);
146 std::vector<uint8_t> pkt2 = mFakeLink->popTxPacket();
147
148 // The retry packet should be an exact match of the first one
149 EXPECT_EQ(pkt1, pkt2);
150 }
151
TEST_F(FakeLinkSyncTests,NoRetryAfterAck)152 TEST_F(FakeLinkSyncTests, NoRetryAfterAck) {
153 txPacket();
154 ASSERT_TRUE(mFakeLink->waitForTxPacket());
155 EXPECT_EQ(mFakeLink->getTxPacketCount(), 1);
156
157 // Generate and reply back with an ACK
158 std::vector<uint8_t> pkt = mFakeLink->popTxPacket();
159 ChppEmptyPacket ack = generateAck(pkt);
160 chppRxDataCb(&mTransportContext, reinterpret_cast<uint8_t *>(&ack),
161 sizeof(ack));
162
163 // We shouldn't get that packet again
164 EXPECT_FALSE(mFakeLink->waitForTxPacket());
165 }
166
TEST_F(FakeLinkSyncTests,MultipleNotifications)167 TEST_F(FakeLinkSyncTests, MultipleNotifications) {
168 constexpr int kNumPackets = 5;
169 for (int i = 0; i < kNumPackets; i++) {
170 txPacket();
171 }
172
173 for (int i = 0; i < kNumPackets; i++) {
174 ASSERT_TRUE(mFakeLink->waitForTxPacket());
175
176 // Generate and reply back with an ACK
177 std::vector<uint8_t> pkt = mFakeLink->popTxPacket();
178 ChppEmptyPacket ack = generateAck(pkt);
179 chppRxDataCb(&mTransportContext, reinterpret_cast<uint8_t *>(&ack),
180 sizeof(ack));
181 }
182
183 EXPECT_FALSE(mFakeLink->waitForTxPacket());
184 }
185
186 } // namespace chpp::test
187