1 /*
2 * Copyright (C) 2020 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 #include <stdio.h>
19 #include <string.h>
20
21 #include "chpp/transport.h"
22 #include "transport_test.h"
23
24 namespace {
25
26 // Max size of payload sent to chppRxDataCb (bytes)
27 constexpr size_t kMaxChunkSize = 20000;
28
29 constexpr size_t kMaxPacketSize = kMaxChunkSize + CHPP_PREAMBLE_LEN_BYTES +
30 sizeof(ChppTransportHeader) +
31 sizeof(ChppTransportFooter);
32
33 // Input sizes to test the entire range of sizes with a few tests
34 constexpr int kChunkSizes[] = {0, 1, 2, 3, 4, 5, 6,
35 7, 8, 10, 16, 20, 30, 40,
36 51, 100, 201, 1000, 10001, 20000};
37
38 /**
39 * Adds a CHPP preamble to the specified location of buf
40 *
41 * @param buf The CHPP preamble will be added to buf
42 * @param loc Location of buf where the CHPP preamble will be added
43 */
chppAddPreamble(uint8_t * buf,size_t loc)44 void chppAddPreamble(uint8_t *buf, size_t loc) {
45 for (size_t i = 0; i < CHPP_PREAMBLE_LEN_BYTES; i++) {
46 buf[loc + i] = static_cast<uint8_t>(
47 CHPP_PREAMBLE_DATA >> (CHPP_PREAMBLE_LEN_BYTES - 1 - i) & 0xff);
48 }
49 }
50
51 /*
52 * Test suite for the CHPP Transport Layer
53 */
54 class TransportTests : public testing::TestWithParam<int> {
55 protected:
SetUp()56 void SetUp() override {
57 chppTransportInit(&context);
58 }
59
60 ChppTransportState context = {};
61 uint8_t buf[kMaxPacketSize] = {};
62 };
63
64 /**
65 * A series of zeros shouldn't change state from CHPP_STATE_PREAMBLE
66 */
TEST_P(TransportTests,ZeroNoPreambleInput)67 TEST_P(TransportTests, ZeroNoPreambleInput) {
68 size_t len = static_cast<size_t>(GetParam());
69 if (len <= kMaxChunkSize) {
70 EXPECT_TRUE(chppRxDataCb(&context, buf, len));
71 EXPECT_EQ(context.rxStatus.state, CHPP_STATE_PREAMBLE);
72 }
73 }
74
75 /**
76 * A preamble after a series of zeros input should change state from
77 * CHPP_STATE_PREAMBLE to CHPP_STATE_HEADER
78 */
TEST_P(TransportTests,ZeroThenPreambleInput)79 TEST_P(TransportTests, ZeroThenPreambleInput) {
80 size_t len = static_cast<size_t>(GetParam());
81
82 if (len <= kMaxChunkSize) {
83 // Add preamble at the end of buf
84 chppAddPreamble(buf, MAX(0, len - CHPP_PREAMBLE_LEN_BYTES));
85
86 if (len >= CHPP_PREAMBLE_LEN_BYTES) {
87 EXPECT_FALSE(chppRxDataCb(&context, buf, len));
88 EXPECT_EQ(context.rxStatus.state, CHPP_STATE_HEADER);
89 } else {
90 EXPECT_TRUE(chppRxDataCb(&context, buf, len));
91 EXPECT_EQ(context.rxStatus.state, CHPP_STATE_PREAMBLE);
92 }
93 }
94 }
95
96 /**
97 * Rx Testing with various length payloads of zeros
98 */
TEST_P(TransportTests,RxPayloadOfZeros)99 TEST_P(TransportTests, RxPayloadOfZeros) {
100 context.rxStatus.state = CHPP_STATE_HEADER;
101 size_t len = static_cast<size_t>(GetParam());
102
103 if (len <= kMaxChunkSize) {
104 ChppTransportHeader header{};
105 header.flags = 0;
106 header.errorCode = 0;
107 header.ackSeq = 1;
108 header.seq = 0;
109 header.length = len;
110
111 memcpy(buf, &header, sizeof(header));
112
113 // Send header and check for correct state
114 EXPECT_FALSE(chppRxDataCb(&context, buf, sizeof(ChppTransportHeader)));
115 if (len > 0) {
116 EXPECT_EQ(context.rxStatus.state, CHPP_STATE_PAYLOAD);
117 } else {
118 EXPECT_EQ(context.rxStatus.state, CHPP_STATE_FOOTER);
119 }
120
121 // Correct decoding of packet length
122 EXPECT_EQ(context.rxHeader.length, len);
123 EXPECT_EQ(context.rxStatus.locInDatagram, 0);
124 EXPECT_EQ(context.rxDatagram.length, len);
125
126 // Send payload if any and check for correct state
127 if (len > 0) {
128 EXPECT_FALSE(
129 chppRxDataCb(&context, &buf[sizeof(ChppTransportHeader)], len));
130 EXPECT_EQ(context.rxStatus.state, CHPP_STATE_FOOTER);
131 }
132
133 // Should have complete packet payload by now
134 EXPECT_EQ(context.rxStatus.locInDatagram, len);
135
136 // But no ACK yet
137 EXPECT_FALSE(context.txStatus.hasPacketsToSend);
138 EXPECT_EQ(context.txStatus.errorCodeToSend, CHPP_ERROR_NONE);
139 EXPECT_EQ(context.rxStatus.expectedSeq, header.seq);
140
141 // Send footer and check for correct state
142 EXPECT_TRUE(chppRxDataCb(&context, &buf[sizeof(ChppTransportHeader) + len],
143 sizeof(ChppTransportFooter)));
144 EXPECT_EQ(context.rxStatus.state, CHPP_STATE_PREAMBLE);
145
146 // Should have reset loc and length for next packet / datagram
147 EXPECT_EQ(context.rxStatus.locInDatagram, 0);
148 EXPECT_EQ(context.rxDatagram.length, 0);
149
150 // If payload packet, expect next packet with incremented sequence #
151 // Otherwise, should keep previous sequence #
152 uint8_t nextSeq = header.seq + ((len > 0) ? 1 : 0);
153 EXPECT_EQ(context.rxStatus.expectedSeq, nextSeq);
154
155 // Check for correct ACK crafting if applicable
156 // TODO: This will need updating once signalling goes in
157 if (len > 0) {
158 EXPECT_TRUE(context.txStatus.hasPacketsToSend);
159 EXPECT_EQ(context.txStatus.errorCodeToSend, CHPP_ERROR_NONE);
160 EXPECT_EQ(context.txDatagramQueue.pending, 0);
161
162 chppTransportDoWork(&context);
163
164 struct ChppTransportHeader *txHeader =
165 (struct ChppTransportHeader *)&context.packetToSend
166 .payload[CHPP_PREAMBLE_LEN_BYTES];
167
168 EXPECT_EQ(txHeader->flags, CHPP_TRANSPORT_FLAG_FINISHED_DATAGRAM);
169 EXPECT_EQ(txHeader->errorCode, CHPP_ERROR_NONE);
170 EXPECT_EQ(txHeader->ackSeq, nextSeq);
171 EXPECT_EQ(txHeader->length, 0);
172
173 EXPECT_EQ(context.packetToSend.length,
174 CHPP_PREAMBLE_LEN_BYTES + sizeof(struct ChppTransportHeader) +
175 sizeof(struct ChppTransportFooter));
176 }
177 }
178 }
179
TEST_P(TransportTests,EnqueueDatagrams)180 TEST_P(TransportTests, EnqueueDatagrams) {
181 size_t len = static_cast<size_t>(GetParam());
182
183 if (len <= CHPP_TX_DATAGRAM_QUEUE_LEN) {
184 // Add (len) datagrams of various length to queue
185
186 int fr = 0;
187
188 for (int j = 0; j == CHPP_TX_DATAGRAM_QUEUE_LEN; j++) {
189 for (size_t i = 1; i <= len; i++) {
190 uint8_t *buf = (uint8_t *)chppMalloc(i + 100);
191 EXPECT_TRUE(chppEnqueueTxDatagram(&context, i + 100, buf));
192
193 EXPECT_EQ(context.txDatagramQueue.pending, i);
194 EXPECT_EQ(context.txDatagramQueue.front, fr);
195 EXPECT_EQ(context.txDatagramQueue
196 .datagram[(i - 1 + fr) % CHPP_TX_DATAGRAM_QUEUE_LEN]
197 .length,
198 i + 100);
199 }
200
201 if (context.txDatagramQueue.pending == CHPP_TX_DATAGRAM_QUEUE_LEN) {
202 uint8_t *buf = (uint8_t *)chppMalloc(100);
203 EXPECT_FALSE(chppEnqueueTxDatagram(&context, 100, buf));
204 chppFree(buf);
205 }
206
207 for (size_t i = len; i > 0; i--) {
208 fr++;
209 fr %= CHPP_TX_DATAGRAM_QUEUE_LEN;
210
211 EXPECT_TRUE(chppDequeueTxDatagram(&context));
212
213 EXPECT_EQ(context.txDatagramQueue.front, fr);
214 EXPECT_EQ(context.txDatagramQueue.pending, i - 1);
215 }
216
217 EXPECT_FALSE(chppDequeueTxDatagram(&context));
218
219 EXPECT_EQ(context.txDatagramQueue.front, fr);
220 EXPECT_EQ(context.txDatagramQueue.pending, 0);
221 }
222 }
223 }
224
225 INSTANTIATE_TEST_SUITE_P(TransportTestRange, TransportTests,
226 testing::ValuesIn(kChunkSizes));
227
228 } // namespace
229