blob: c13feaa130110088f2456e295655ebf4fa54f06c [file] [log] [blame]
kasunathbac958d2022-06-07 18:15:24 -07001#include "nlohmann/json.hpp"
2#include "rde/external_storer_interface.hpp"
3#include "rde/rde_handler.hpp"
4
5#include <memory>
6#include <span>
7
8#include <gmock/gmock-matchers.h>
9#include <gmock/gmock.h>
10#include <gtest/gtest.h>
11
12namespace bios_bmc_smm_error_logger
13{
14namespace rde
15{
16
Brandon Kim90ccfe82025-06-06 20:38:56 +000017using ::testing::_;
18using ::testing::NiceMock;
kasunathbac958d2022-06-07 18:15:24 -070019using ::testing::Return;
20
Brandon Kim90ccfe82025-06-06 20:38:56 +000021// Mock for ExternalStorerInterface
22class MockExternalStorerInterface : public ExternalStorerInterface
23{
24 public:
25 MOCK_METHOD(bool, publishJson, (std::string_view jsonStr), (override));
26};
27
28// Test fixture for RdeCommandHandler
29class RdeCommandHandlerTest : public ::testing::Test
30{
31 protected:
32 std::unique_ptr<MockExternalStorerInterface> mockExStorerInstance;
33 // Note: RdeCommandHandler takes ownership of the raw pointer.
34 // We pass mockExStorerInstance.get() and it's moved into the handler.
35 // For safety in test setup, we'll create the handler with a moved
36 // unique_ptr.
37 std::unique_ptr<RdeCommandHandler> handler;
38
39 void SetUp() override
40 {
41 auto exStorer =
42 std::make_unique<NiceMock<MockExternalStorerInterface>>();
43 // Keep a raw pointer for EXPECT_CALL, but handler owns the unique_ptr
44 mockExStorer = exStorer.get();
45 handler = std::make_unique<RdeCommandHandler>(std::move(exStorer));
46 }
47
48 // Helper to create RdeOperationInitReqHeader and its command data
49 std::vector<uint8_t> createOpInitReqCmd(
50 bool containsPayload, uint8_t opType, uint8_t sendDataTransferHandle,
51 uint32_t resourceID, uint8_t opLocatorLength,
52 uint16_t requestPayloadLength,
53 const std::vector<uint8_t>& payloadData = {})
54 {
55 RdeOperationInitReqHeader header{};
56 header.containsRequestPayload = containsPayload;
57 header.operationType = opType;
58 header.sendDataTransferHandle = sendDataTransferHandle;
59 header.resourceID = resourceID;
60 header.operationLocatorLength = opLocatorLength;
61 header.requestPayloadLength = requestPayloadLength;
62
63 std::vector<uint8_t> command(sizeof(header));
64 memcpy(command.data(), &header, sizeof(header));
65 command.insert(command.end(), payloadData.begin(), payloadData.end());
66 return command;
67 }
68
69 // Helper to create MultipartReceiveResHeader and its command data
70 std::vector<uint8_t> createMultiPartRespCmd(
71 uint8_t transferFlag, uint32_t nextDataTransferHandleAsResourceId,
72 uint16_t dataLength, const std::vector<uint8_t>& payloadData,
73 const std::optional<uint32_t>& checksum = std::nullopt)
74 {
75 MultipartReceiveResHeader header{};
76 header.transferFlag = transferFlag;
77 header.nextDataTransferHandle = nextDataTransferHandleAsResourceId;
78 header.dataLengthBytes = dataLength;
79
80 std::vector<uint8_t> command(sizeof(header));
81 memcpy(command.data(), &header, sizeof(header));
82 command.insert(command.end(), payloadData.begin(), payloadData.end());
83
84 if (checksum)
85 {
86 uint32_t csVal = *checksum;
87 command.push_back(static_cast<uint8_t>(csVal & 0xFF));
88 command.push_back(static_cast<uint8_t>((csVal >> 8) & 0xFF));
89 command.push_back(static_cast<uint8_t>((csVal >> 16) & 0xFF));
90 command.push_back(static_cast<uint8_t>((csVal >> 24) & 0xFF));
91 }
92
93 return command;
94 }
95
96 // To be used by EXPECT_CALL
97 MockExternalStorerInterface* mockExStorer;
98};
99
100TEST_F(RdeCommandHandlerTest, DecodeRdeCommand_InvalidType)
101{
102 std::vector<uint8_t> cmdData = {0x01, 0x02};
103 auto status =
104 handler->decodeRdeCommand(cmdData, static_cast<RdeCommandType>(0xFF));
105 EXPECT_EQ(status, RdeDecodeStatus::RdeInvalidCommand);
106}
107
108TEST_F(RdeCommandHandlerTest, GetDictionaryCount_Initial)
109{
110 EXPECT_EQ(handler->getDictionaryCount(), 0);
111}
112
113TEST_F(RdeCommandHandlerTest, OperationInitRequest_NoPayload)
114{
115 auto cmd = createOpInitReqCmd(
116 false, // containsRequestPayload
117 static_cast<uint8_t>(RdeOperationInitType::RdeOpInitOperationUpdate), 0,
118 1, 0, 0);
119 auto status =
120 handler->decodeRdeCommand(cmd, RdeCommandType::RdeOperationInitRequest);
121 EXPECT_EQ(status, RdeDecodeStatus::RdeOk);
122}
123
Brandon Kim8540c7d2025-06-08 23:54:24 +0000124TEST_F(RdeCommandHandlerTest, OperationInitRequest_CmdTooSmallForHeader)
125{
126 std::vector<uint8_t> cmdData = {0x01, 0x02}; // Smaller than header
127 auto status = handler->decodeRdeCommand(
128 cmdData, RdeCommandType::RdeOperationInitRequest);
129 EXPECT_EQ(status, RdeDecodeStatus::RdeInvalidCommand);
130}
131
132TEST_F(RdeCommandHandlerTest,
133 OperationInitRequest_CmdTooSmallForDeclaredPayload)
134{
135 // Header declares payload, but actual data is too short.
136 // Header: containsPayload=true, opLocatorLength=1, requestPayloadLength=5
137 // Actual payload data provided: only 1 byte for locator, 0 for payload.
138 RdeOperationInitReqHeader header{};
139 header.containsRequestPayload = true;
140 header.operationLocatorLength = 1;
141 header.requestPayloadLength = 5; // Expects 5 bytes of payload
142 std::vector<uint8_t> cmdData(sizeof(header));
143 memcpy(cmdData.data(), &header, sizeof(header));
144 cmdData.push_back(0xAA); // Only 1 byte for locator, payload missing
145 auto status = handler->decodeRdeCommand(
146 cmdData, RdeCommandType::RdeOperationInitRequest);
147 EXPECT_EQ(status, RdeDecodeStatus::RdeInvalidCommand);
148}
149
Brandon Kim90ccfe82025-06-06 20:38:56 +0000150TEST_F(RdeCommandHandlerTest, OperationInitRequest_UnsupportedOperationType)
151{
152 auto cmd = createOpInitReqCmd(true, 0xFE, 0, 1, 0, 5, {1, 2, 3, 4, 5});
153 auto status =
154 handler->decodeRdeCommand(cmd, RdeCommandType::RdeOperationInitRequest);
155 EXPECT_EQ(status, RdeDecodeStatus::RdeUnsupportedOperation);
156}
157
158TEST_F(RdeCommandHandlerTest, OperationInitRequest_PayloadOverflowNotSupported)
159{
160 auto cmd = createOpInitReqCmd(
161 true,
162 static_cast<uint8_t>(RdeOperationInitType::RdeOpInitOperationUpdate), 1,
163 1, 0, 5, {1, 2, 3, 4, 5}); // sendDataTransferHandle != 0
164 auto status =
165 handler->decodeRdeCommand(cmd, RdeCommandType::RdeOperationInitRequest);
166 EXPECT_EQ(status, RdeDecodeStatus::RdePayloadOverflow);
167}
168
169TEST_F(RdeCommandHandlerTest, OperationInitRequest_SchemaDictionaryNotFound)
170{
171 std::vector<uint8_t> locatorAndPayload = {0x00}; // Minimal locator
172 auto cmd = createOpInitReqCmd(
173 true,
174 static_cast<uint8_t>(RdeOperationInitType::RdeOpInitOperationUpdate), 0,
175 123, 1, 0, locatorAndPayload); // resourceID 123, opLocatorLength=1,
176 // payloadLength=0
177 auto status =
178 handler->decodeRdeCommand(cmd, RdeCommandType::RdeOperationInitRequest);
179 EXPECT_EQ(status, RdeDecodeStatus::RdeNoDictionary);
180}
181
182TEST_F(RdeCommandHandlerTest, OperationInitRequest_AnnotationDictionaryNotFound)
183{
184 uint32_t schemaResourceId = 1;
185 std::vector<uint8_t> schemaDictData = {'s', 'c', 'h', 'e', 'm', 'a'};
186 uint32_t schemaChecksum = 0xb88e4152; // CRC32("schema")
187 auto cmdSchema = createMultiPartRespCmd(
188 static_cast<uint8_t>(
189 RdeMultiReceiveTransferFlag::RdeMRecFlagStartAndEnd),
190 schemaResourceId, schemaDictData.size(), schemaDictData,
191 schemaChecksum);
192 ASSERT_EQ(handler->decodeRdeCommand(
193 cmdSchema, RdeCommandType::RdeMultiPartReceiveResponse),
194 RdeDecodeStatus::RdeStopFlagReceived);
195
196 std::vector<uint8_t> locatorAndPayload = {0x00};
197 auto cmdOpInit = createOpInitReqCmd(
198 true,
199 static_cast<uint8_t>(RdeOperationInitType::RdeOpInitOperationUpdate), 0,
200 schemaResourceId, 1, 0, locatorAndPayload);
201 auto status = handler->decodeRdeCommand(
202 cmdOpInit, RdeCommandType::RdeOperationInitRequest);
203 EXPECT_EQ(status, RdeDecodeStatus::RdeNoDictionary);
204}
205
206TEST_F(RdeCommandHandlerTest, OperationInitRequest_BejDecodingError)
207{
208 // Add dummy schema dictionary
209 uint32_t schemaResourceId = 1;
210 std::vector<uint8_t> schemaDictData = {'s', 'c', 'h', 'e', 'm', 'a'};
211 uint32_t schemaChecksum = 0xb88e4152; // CRC32("schema")
212 auto cmdSchema = createMultiPartRespCmd(
213 static_cast<uint8_t>(
214 RdeMultiReceiveTransferFlag::RdeMRecFlagStartAndEnd),
215 schemaResourceId, schemaDictData.size(), schemaDictData,
216 schemaChecksum);
217 ASSERT_EQ(handler->decodeRdeCommand(
218 cmdSchema, RdeCommandType::RdeMultiPartReceiveResponse),
219 RdeDecodeStatus::RdeStopFlagReceived);
220
221 // Add dummy annotation dictionary
222 uint32_t annotationResourceId =
223 0; // DictionaryManager::annotationResourceId
224 std::vector<uint8_t> annotationDictData = {'a', 'n', 'n', 'o'};
225 uint32_t annotationChecksum = 0xc6e493b0; // CRC32("anno")
226 auto cmdAnnotation = createMultiPartRespCmd(
227 static_cast<uint8_t>(
228 RdeMultiReceiveTransferFlag::RdeMRecFlagStartAndEnd),
229 annotationResourceId, annotationDictData.size(), annotationDictData,
230 annotationChecksum);
231 ASSERT_EQ(handler->decodeRdeCommand(
232 cmdAnnotation, RdeCommandType::RdeMultiPartReceiveResponse),
233 RdeDecodeStatus::RdeStopFlagReceived);
234
235 std::vector<uint8_t> locator = {0x00};
236 std::vector<uint8_t> bejPayload = {0x01, 0x02}; // Dummy BEJ payload
237 std::vector<uint8_t> opInitFullPayload = locator;
238 opInitFullPayload.insert(opInitFullPayload.end(), bejPayload.begin(),
239 bejPayload.end());
240
241 auto cmdOpInit = createOpInitReqCmd(
242 true,
243 static_cast<uint8_t>(RdeOperationInitType::RdeOpInitOperationUpdate), 0,
244 schemaResourceId, locator.size(), bejPayload.size(), opInitFullPayload);
245
246 // Expect BEJ decoding to fail with invalid dictionaries
247 EXPECT_CALL(*mockExStorer, publishJson(_)).Times(0);
248 auto status = handler->decodeRdeCommand(
249 cmdOpInit, RdeCommandType::RdeOperationInitRequest);
250 EXPECT_EQ(status, RdeDecodeStatus::RdeBejDecodingError);
251}
252
253TEST_F(RdeCommandHandlerTest, OperationInitRequest_ExternalStorerPublishFails)
254{
255 // This test requires BejDecoder to succeed. Since we can't easily mock
256 // BejDecoder or provide universally valid simple BEJ dicts/payloads that
257 // guarantee success for the internal BejDecoder, this specific path is hard
258 // to test in isolation. We would need a known schema, annotation, and
259 // payload that successfully decodes.
260 GTEST_SKIP()
261 << "Skipping due to complexity of ensuring BEJ decode success without mock or valid complex BEJ data.";
262}
263
264TEST_F(RdeCommandHandlerTest, MultiPartReceiveResp_CmdTooSmallForHeader)
265{
266 std::vector<uint8_t> cmdData = {0x01};
267 auto status = handler->decodeRdeCommand(
268 cmdData, RdeCommandType::RdeMultiPartReceiveResponse);
269 EXPECT_EQ(status, RdeDecodeStatus::RdeInvalidCommand);
270}
271
272TEST_F(RdeCommandHandlerTest,
273 MultiPartReceiveResp_CmdTooSmallForDeclaredPayload)
274{
275 MultipartReceiveResHeader header{};
276 header.transferFlag =
277 static_cast<uint8_t>(RdeMultiReceiveTransferFlag::RdeMRecFlagStart);
278 header.nextDataTransferHandle = 1;
279 header.dataLengthBytes = 10; // Expects 10 bytes
280
281 std::vector<uint8_t> cmdData(sizeof(header));
282 memcpy(cmdData.data(), &header, sizeof(header));
283 cmdData.push_back(0xAA); // Only 1 byte of payload provided
284
285 auto status = handler->decodeRdeCommand(
286 cmdData, RdeCommandType::RdeMultiPartReceiveResponse);
287 EXPECT_EQ(status, RdeDecodeStatus::RdeInvalidCommand);
288}
289
290TEST_F(RdeCommandHandlerTest, MultiPartReceiveResp_InvalidTransferFlag)
291{
292 std::vector<uint8_t> payload = {'d', 'a', 't', 'a'};
293 auto cmd = createMultiPartRespCmd(0xFF, 1, payload.size(),
294 payload); // Invalid flag
295 auto status = handler->decodeRdeCommand(
296 cmd, RdeCommandType::RdeMultiPartReceiveResponse);
297 EXPECT_EQ(status, RdeDecodeStatus::RdeInvalidCommand);
298}
299
300TEST_F(RdeCommandHandlerTest, MultiPartReceiveResp_FlagStart)
301{
302 uint32_t resourceId = 1;
303 std::vector<uint8_t> dataPayload = {'s', 't', 'a', 'r', 't'};
304 auto cmd = createMultiPartRespCmd(
305 static_cast<uint8_t>(RdeMultiReceiveTransferFlag::RdeMRecFlagStart),
306 resourceId, dataPayload.size(), dataPayload);
307 auto status = handler->decodeRdeCommand(
308 cmd, RdeCommandType::RdeMultiPartReceiveResponse);
309 EXPECT_EQ(status, RdeDecodeStatus::RdeOk);
310 EXPECT_EQ(handler->getDictionaryCount(), 0); // Not yet complete
311}
312
313TEST_F(RdeCommandHandlerTest, MultiPartReceiveResp_FlagMiddle_InvalidOrder)
314{
315 uint32_t resourceId = 1;
316 std::vector<uint8_t> dataPayload = {'m', 'i', 'd', 'd', 'l', 'e'};
317 auto cmd = createMultiPartRespCmd(
318 static_cast<uint8_t>(RdeMultiReceiveTransferFlag::RdeMRecFlagMiddle),
319 resourceId, dataPayload.size(), dataPayload);
320 auto status = handler->decodeRdeCommand(
321 cmd, RdeCommandType::RdeMultiPartReceiveResponse);
322 EXPECT_EQ(status, RdeDecodeStatus::RdeInvalidPktOrder);
323}
324
325TEST_F(RdeCommandHandlerTest,
326 MultiPartReceiveResp_FlagMiddle_AfterStart_SameResource)
327{
328 uint32_t resourceId = 1;
329 std::vector<uint8_t> startPayload = {'s', 't', 'a', 'r', 't'};
330 auto cmdStart = createMultiPartRespCmd(
331 static_cast<uint8_t>(RdeMultiReceiveTransferFlag::RdeMRecFlagStart),
332 resourceId, startPayload.size(), startPayload);
333 ASSERT_EQ(handler->decodeRdeCommand(
334 cmdStart, RdeCommandType::RdeMultiPartReceiveResponse),
335 RdeDecodeStatus::RdeOk);
336
337 std::vector<uint8_t> middlePayload = {'m', 'i', 'd', 'd', 'l', 'e'};
338 auto cmdMiddle = createMultiPartRespCmd(
339 static_cast<uint8_t>(RdeMultiReceiveTransferFlag::RdeMRecFlagMiddle),
340 resourceId, middlePayload.size(), middlePayload);
341 auto status = handler->decodeRdeCommand(
342 cmdMiddle, RdeCommandType::RdeMultiPartReceiveResponse);
343 EXPECT_EQ(status, RdeDecodeStatus::RdeOk);
344 EXPECT_EQ(handler->getDictionaryCount(), 0);
345}
346
347TEST_F(RdeCommandHandlerTest,
348 MultiPartReceiveResp_FlagMiddle_AfterStart_NewResource)
349{
350 // Tests current behavior: if Middle flag comes for a new resource,
351 // previous resource is marked complete, new one is started. CRC continues.
352 uint32_t resourceId1 = 1;
353 std::vector<uint8_t> startPayload1 = {'r', '1', 's'};
354 auto cmdStart1 = createMultiPartRespCmd(
355 static_cast<uint8_t>(RdeMultiReceiveTransferFlag::RdeMRecFlagStart),
356 resourceId1, startPayload1.size(), startPayload1);
357 ASSERT_EQ(handler->decodeRdeCommand(
358 cmdStart1, RdeCommandType::RdeMultiPartReceiveResponse),
359 RdeDecodeStatus::RdeOk);
360
361 uint32_t resourceId2 = 2;
362 std::vector<uint8_t> middlePayload2 = {'r', '2', 'm'};
363 auto cmdMiddle2 = createMultiPartRespCmd(
364 static_cast<uint8_t>(RdeMultiReceiveTransferFlag::RdeMRecFlagMiddle),
365 resourceId2, middlePayload2.size(), middlePayload2);
366 auto status = handler->decodeRdeCommand(
367 cmdMiddle2, RdeCommandType::RdeMultiPartReceiveResponse);
368 EXPECT_EQ(status, RdeDecodeStatus::RdeOk);
369 EXPECT_EQ(handler->getDictionaryCount(), 1); // Resource 1 completed
370}
371
372TEST_F(RdeCommandHandlerTest, MultiPartReceiveResp_FlagEnd_InvalidOrder)
373{
374 uint32_t resourceId = 1;
375 std::vector<uint8_t> dataPayload = {'e', 'n', 'd'};
376 uint32_t checksum = 0xfc33b1; // CRC32("end")
377 auto cmd = createMultiPartRespCmd(
378 static_cast<uint8_t>(RdeMultiReceiveTransferFlag::RdeMRecFlagEnd),
379 resourceId, dataPayload.size(), dataPayload, checksum);
380 auto status = handler->decodeRdeCommand(
381 cmd, RdeCommandType::RdeMultiPartReceiveResponse);
382 EXPECT_EQ(status, RdeDecodeStatus::RdeInvalidPktOrder);
383}
384
385TEST_F(RdeCommandHandlerTest,
386 MultiPartReceiveResp_FlagEnd_AfterStart_SameResource_ValidChecksum)
387{
388 uint32_t resourceId = 1;
389 std::vector<uint8_t> startPayload = {'s', 't', 'a', 'r', 't'};
390 auto cmdStart = createMultiPartRespCmd(
391 static_cast<uint8_t>(RdeMultiReceiveTransferFlag::RdeMRecFlagStart),
392 resourceId, startPayload.size(), startPayload);
393 ASSERT_EQ(handler->decodeRdeCommand(
394 cmdStart, RdeCommandType::RdeMultiPartReceiveResponse),
395 RdeDecodeStatus::RdeOk);
396
397 std::vector<uint8_t> endPayload = {'e', 'n', 'd'};
398 uint32_t checksum = 0x4800f1a; // CRC32("startend")
399 auto cmdEnd = createMultiPartRespCmd(
400 static_cast<uint8_t>(RdeMultiReceiveTransferFlag::RdeMRecFlagEnd),
401 resourceId, endPayload.size(), endPayload, checksum);
402 auto status = handler->decodeRdeCommand(
403 cmdEnd, RdeCommandType::RdeMultiPartReceiveResponse);
404 EXPECT_EQ(status, RdeDecodeStatus::RdeStopFlagReceived);
405 EXPECT_EQ(handler->getDictionaryCount(), 1);
406}
407
408TEST_F(RdeCommandHandlerTest,
409 MultiPartReceiveResp_FlagEnd_AfterStart_SameResource_InvalidChecksum)
410{
411 uint32_t resourceId = 1;
412 std::vector<uint8_t> startPayload = {'s', 't', 'a', 'r', 't'};
413 auto cmdStart = createMultiPartRespCmd(
414 static_cast<uint8_t>(RdeMultiReceiveTransferFlag::RdeMRecFlagStart),
415 resourceId, startPayload.size(), startPayload);
416 ASSERT_EQ(handler->decodeRdeCommand(
417 cmdStart, RdeCommandType::RdeMultiPartReceiveResponse),
418 RdeDecodeStatus::RdeOk);
419
420 std::vector<uint8_t> endPayload = {'e', 'n', 'd'};
421 uint32_t invalidChecksum = 0x12345678; // Invalid checksum
422 auto cmdEnd = createMultiPartRespCmd(
423 static_cast<uint8_t>(RdeMultiReceiveTransferFlag::RdeMRecFlagEnd),
424 resourceId, endPayload.size(), endPayload, invalidChecksum);
425 auto status = handler->decodeRdeCommand(
426 cmdEnd, RdeCommandType::RdeMultiPartReceiveResponse);
427 EXPECT_EQ(status, RdeDecodeStatus::RdeInvalidChecksum);
428 EXPECT_EQ(handler->getDictionaryCount(), 0); // Dictionaries invalidated
429}
430
431TEST_F(RdeCommandHandlerTest,
432 MultiPartReceiveResp_FlagEnd_AfterStart_NewResource_UsesPrevCrcState)
433{
434 // This test verifies that if an End flag for a new resource follows a Start
435 // flag for a different resource, the CRC calculation for the new resource's
436 // data incorrectly continues from the previous resource's CRC state.
437 uint32_t resourceId1 = 1;
438 std::vector<uint8_t> startPayload1 = {'r', '1', 's'}; // CRC for "r1s"
439 auto cmdStart1 = createMultiPartRespCmd(
440 static_cast<uint8_t>(RdeMultiReceiveTransferFlag::RdeMRecFlagStart),
441 resourceId1, startPayload1.size(), startPayload1);
442 ASSERT_EQ(handler->decodeRdeCommand(
443 cmdStart1, RdeCommandType::RdeMultiPartReceiveResponse),
444 RdeDecodeStatus::RdeOk);
445
446 uint32_t resourceId2 = 2;
447 std::vector<uint8_t> endPayload2 = {'r', '2', 'e'};
448 // Checksum for "r2e" ALONE is 0x789ca48a.
449 // If CRC continued from "r1s", this checksum will be wrong.
450 uint32_t checksumForR2eAlone = 0x789ca48a;
451 auto cmdEnd2 = createMultiPartRespCmd(
452 static_cast<uint8_t>(RdeMultiReceiveTransferFlag::RdeMRecFlagEnd),
453 resourceId2, endPayload2.size(), endPayload2, checksumForR2eAlone);
454
455 auto status = handler->decodeRdeCommand(
456 cmdEnd2, RdeCommandType::RdeMultiPartReceiveResponse);
457 // Expect InvalidChecksum because internal CRC is for "r1s" + "r2e"
458 EXPECT_EQ(status, RdeDecodeStatus::RdeInvalidChecksum);
459 EXPECT_EQ(handler->getDictionaryCount(), 0); // Dictionaries invalidated
460}
461
462TEST_F(RdeCommandHandlerTest,
463 MultiPartReceiveResp_FlagStartAndEnd_ValidChecksum)
464{
465 uint32_t resourceId = 1;
466 std::vector<uint8_t> dataPayload = {'c', 'o', 'm', 'p', 'l', 'e', 't', 'e'};
467 uint32_t checksum = 0x4267d023; // CRC32("complete")
468 auto cmd = createMultiPartRespCmd(
469 static_cast<uint8_t>(
470 RdeMultiReceiveTransferFlag::RdeMRecFlagStartAndEnd),
471 resourceId, dataPayload.size(), dataPayload, checksum);
472 auto status = handler->decodeRdeCommand(
473 cmd, RdeCommandType::RdeMultiPartReceiveResponse);
474 EXPECT_EQ(status, RdeDecodeStatus::RdeStopFlagReceived);
475 EXPECT_EQ(handler->getDictionaryCount(), 1);
476}
477
478TEST_F(RdeCommandHandlerTest,
479 MultiPartReceiveResp_FlagStartAndEnd_InvalidChecksum)
480{
481 uint32_t resourceId = 1;
482 std::vector<uint8_t> dataPayload = {'c', 'o', 'm', 'p', 'l', 'e', 't', 'e'};
483 uint32_t invalidChecksum = 0x12345678; // Invalid checksum
484 auto cmd = createMultiPartRespCmd(
485 static_cast<uint8_t>(
486 RdeMultiReceiveTransferFlag::RdeMRecFlagStartAndEnd),
487 resourceId, dataPayload.size(), dataPayload, invalidChecksum);
488 auto status = handler->decodeRdeCommand(
489 cmd, RdeCommandType::RdeMultiPartReceiveResponse);
490 EXPECT_EQ(status, RdeDecodeStatus::RdeInvalidChecksum);
491 EXPECT_EQ(handler->getDictionaryCount(), 0);
492}
493
494TEST_F(RdeCommandHandlerTest,
495 MultiPartReceiveResp_Sequence_StartMiddleEnd_Valid)
496{
497 uint32_t resourceId = 42;
498
499 std::vector<uint8_t> startPayload = {'p', 'a', 'r', 't', '1'};
500 auto cmdStart = createMultiPartRespCmd(
501 static_cast<uint8_t>(RdeMultiReceiveTransferFlag::RdeMRecFlagStart),
502 resourceId, startPayload.size(), startPayload);
503 ASSERT_EQ(handler->decodeRdeCommand(
504 cmdStart, RdeCommandType::RdeMultiPartReceiveResponse),
505 RdeDecodeStatus::RdeOk);
506
507 std::vector<uint8_t> middlePayload = {'p', 'a', 'r', 't', '2'};
508 auto cmdMiddle = createMultiPartRespCmd(
509 static_cast<uint8_t>(RdeMultiReceiveTransferFlag::RdeMRecFlagMiddle),
510 resourceId, middlePayload.size(), middlePayload);
511 ASSERT_EQ(handler->decodeRdeCommand(
512 cmdMiddle, RdeCommandType::RdeMultiPartReceiveResponse),
513 RdeDecodeStatus::RdeOk);
514
515 std::vector<uint8_t> endPayload = {'p', 'a', 'r', 't', '3'};
516 uint32_t checksum = 0xf5295f3; // CRC32("part1part2part3")
517 auto cmdEnd = createMultiPartRespCmd(
518 static_cast<uint8_t>(RdeMultiReceiveTransferFlag::RdeMRecFlagEnd),
519 resourceId, endPayload.size(), endPayload, checksum);
520 auto status = handler->decodeRdeCommand(
521 cmdEnd, RdeCommandType::RdeMultiPartReceiveResponse);
522 EXPECT_EQ(status, RdeDecodeStatus::RdeStopFlagReceived);
523 EXPECT_EQ(handler->getDictionaryCount(), 1);
524}
525
526TEST_F(RdeCommandHandlerTest,
527 MultiPartReceiveResp_MultipleDictionaries_ValidSequence)
528{
529 // Dictionary 1: StartAndEnd
530 uint32_t resourceId1 = 1;
531 std::vector<uint8_t> payload1 = {'d', 'i', 'c', 't', '1'};
532 uint32_t checksum1 = 0xbca257a8; // CRC32("dict1")
533 auto cmd1 = createMultiPartRespCmd(
534 static_cast<uint8_t>(
535 RdeMultiReceiveTransferFlag::RdeMRecFlagStartAndEnd),
536 resourceId1, payload1.size(), payload1, checksum1);
537 ASSERT_EQ(handler->decodeRdeCommand(
538 cmd1, RdeCommandType::RdeMultiPartReceiveResponse),
539 RdeDecodeStatus::RdeStopFlagReceived);
540 ASSERT_EQ(handler->getDictionaryCount(), 1);
541
542 // Dictionary 2: Start, Middle, End
543 uint32_t resourceId2 = 2;
544 std::vector<uint8_t> startPayload2 = {'d', '2', '_'};
545 auto cmdStart2 = createMultiPartRespCmd(
546 static_cast<uint8_t>(RdeMultiReceiveTransferFlag::RdeMRecFlagStart),
547 resourceId2, startPayload2.size(), startPayload2);
548 ASSERT_EQ(handler->decodeRdeCommand(
549 cmdStart2, RdeCommandType::RdeMultiPartReceiveResponse),
550 RdeDecodeStatus::RdeOk);
551 ASSERT_EQ(handler->getDictionaryCount(),
552 1); // Dict1 still valid, Dict2 not yet
553
554 std::vector<uint8_t> middlePayload2 = {'m', 'i', 'd'};
555 auto cmdMiddle2 = createMultiPartRespCmd(
556 static_cast<uint8_t>(RdeMultiReceiveTransferFlag::RdeMRecFlagMiddle),
557 resourceId2, middlePayload2.size(), middlePayload2);
558 ASSERT_EQ(handler->decodeRdeCommand(
559 cmdMiddle2, RdeCommandType::RdeMultiPartReceiveResponse),
560 RdeDecodeStatus::RdeOk);
561
562 std::vector<uint8_t> endPayload2 = {'e', 'n', 'd'};
563 uint32_t checksum2 = 0x9e428a17; // CRC32("d2_midend")
564 auto cmdEnd2 = createMultiPartRespCmd(
565 static_cast<uint8_t>(RdeMultiReceiveTransferFlag::RdeMRecFlagEnd),
566 resourceId2, endPayload2.size(), endPayload2, checksum2);
567 ASSERT_EQ(handler->decodeRdeCommand(
568 cmdEnd2, RdeCommandType::RdeMultiPartReceiveResponse),
569 RdeDecodeStatus::RdeStopFlagReceived);
570 ASSERT_EQ(handler->getDictionaryCount(),
571 2); // Both dictionaries should now be valid
572}
573
Kasun Athukoralae710a362025-07-16 01:18:49 +0000574TEST_F(RdeCommandHandlerTest, MultiPartReceiveResp_HandleCrc_MismatchedSize)
575{
576 // Header will claim 10 bytes of data.
577 MultipartReceiveResHeader header{};
578 header.transferFlag = static_cast<uint8_t>(
579 RdeMultiReceiveTransferFlag::RdeMRecFlagStartAndEnd);
580 header.nextDataTransferHandle = 1; // dummy resource ID
581 header.dataLengthBytes = 10;
582
583 // Create a command that is exactly the size of the header + data, which
584 // means it is missing the 4-byte checksum. This will pass the initial size
585 // check but fail the one in handleCrc.
586 size_t actualSize =
587 sizeof(MultipartReceiveResHeader) + header.dataLengthBytes;
588 std::vector<uint8_t> command(actualSize);
589 memcpy(command.data(), &header, sizeof(header));
590
591 auto status = handler->decodeRdeCommand(
592 command, RdeCommandType::RdeMultiPartReceiveResponse);
593 EXPECT_EQ(status, RdeDecodeStatus::RdeInvalidCommand);
594}
595
kasunathbac958d2022-06-07 18:15:24 -0700596/**
597 * @brief Dummy values for annotation dictionary. We do not need the annotation
598 * dictionary. So this contains a dictionary with some dummy values. But the RDE
599 * header is correct.
600 */
601constexpr std::array<uint8_t, 38> mRcvDummyAnnotation{
602 {0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00,
Kasun Athukoralae3410a82025-07-16 00:10:23 +0000603 0x0, 0x0, 0xc, 0x0, 0x0, 0xf0, 0xf0, 0xf1, 0x18, 0x00,
kasunathbac958d2022-06-07 18:15:24 -0700604 0x0, 0x0, 0x0, 0x0, 0x0, 0x16, 0x0, 0x5, 0x0, 0xc,
Kasun Athukoralae3410a82025-07-16 00:10:23 +0000605 0x84, 0x0, 0x14, 0x0, 0xe2, 0x14, 0xd2, 0x0b}};
kasunathbac958d2022-06-07 18:15:24 -0700606
607constexpr std::array<uint8_t, 38> mRcvDummyInvalidChecksum{
608 {0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00,
609 0x0, 0x0, 0xc, 0x0, 0x0, 0xf0, 0xf0, 0xf1, 0x17, 0x1,
610 0x0, 0x0, 0x0, 0x0, 0x0, 0x16, 0x0, 0x5, 0x0, 0xc,
611 0x84, 0x0, 0x14, 0x0, 0x17, 0x86, 0x00, 0x00}};
612
613/**
614 * @brief MultipartReceive command with START_AND_END flag set.
615 */
616constexpr std::array<uint8_t, 293> mRcvInput0StartAndEnd{
617 {0x00, 0x03, 0x02, 0x00, 0x00, 0x00, 0x17, 0x01, 0x00, 0x00, 0x0, 0x0,
618 0xc, 0x0, 0x0, 0xf0, 0xf0, 0xf1, 0x17, 0x1, 0x0, 0x0, 0x0, 0x0,
619 0x0, 0x16, 0x0, 0x5, 0x0, 0xc, 0x84, 0x0, 0x14, 0x0, 0x0, 0x48,
620 0x0, 0x1, 0x0, 0x13, 0x90, 0x0, 0x56, 0x1, 0x0, 0x0, 0x0, 0x0,
621 0x0, 0x3, 0xa3, 0x0, 0x74, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x16,
622 0xa6, 0x0, 0x34, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x16, 0xbc, 0x0,
623 0x64, 0x4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x13, 0xd2, 0x0, 0x0, 0x0,
624 0x0, 0x52, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x74, 0x0, 0x0, 0x0,
625 0x0, 0x0, 0x0, 0xf, 0xe5, 0x0, 0x46, 0x1, 0x0, 0x66, 0x0, 0x3,
626 0x0, 0xb, 0xf4, 0x0, 0x50, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x9,
627 0xff, 0x0, 0x50, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7, 0x8, 0x1,
628 0x50, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7, 0xf, 0x1, 0x44, 0x75,
629 0x6d, 0x6d, 0x79, 0x53, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x0, 0x43, 0x68,
630 0x69, 0x6c, 0x64, 0x41, 0x72, 0x72, 0x61, 0x79, 0x50, 0x72, 0x6f, 0x70,
631 0x65, 0x72, 0x74, 0x79, 0x0, 0x49, 0x64, 0x0, 0x53, 0x61, 0x6d, 0x70,
632 0x6c, 0x65, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x50, 0x72, 0x6f,
633 0x70, 0x65, 0x72, 0x74, 0x79, 0x0, 0x53, 0x61, 0x6d, 0x70, 0x6c, 0x65,
634 0x49, 0x6e, 0x74, 0x65, 0x67, 0x65, 0x72, 0x50, 0x72, 0x6f, 0x70, 0x65,
635 0x72, 0x74, 0x79, 0x0, 0x53, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x52, 0x65,
636 0x61, 0x6c, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 0x0, 0x41,
637 0x6e, 0x6f, 0x74, 0x68, 0x65, 0x72, 0x42, 0x6f, 0x6f, 0x6c, 0x65, 0x61,
638 0x6e, 0x0, 0x4c, 0x69, 0x6e, 0x6b, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73,
639 0x0, 0x4c, 0x69, 0x6e, 0x6b, 0x44, 0x6f, 0x77, 0x6e, 0x0, 0x4c, 0x69,
640 0x6e, 0x6b, 0x55, 0x70, 0x0, 0x4e, 0x6f, 0x4c, 0x69, 0x6e, 0x6b, 0x0,
641 0x0, 0x8c, 0x87, 0xed, 0x74}};
642
643/**
644 * @brief MultipartReceive command with START flag set.
645 */
646constexpr std::array<uint8_t, 166> mRcvInput1Start{
647 {0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x9c, 0x00, 0x00, 0x00, 0x0, 0x0,
648 0xc, 0x0, 0x0, 0xf0, 0xf0, 0xf1, 0x17, 0x1, 0x0, 0x0, 0x0, 0x0,
649 0x0, 0x16, 0x0, 0x5, 0x0, 0xc, 0x84, 0x0, 0x14, 0x0, 0x0, 0x48,
650 0x0, 0x1, 0x0, 0x13, 0x90, 0x0, 0x56, 0x1, 0x0, 0x0, 0x0, 0x0,
651 0x0, 0x3, 0xa3, 0x0, 0x74, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x16,
652 0xa6, 0x0, 0x34, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x16, 0xbc, 0x0,
653 0x64, 0x4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x13, 0xd2, 0x0, 0x0, 0x0,
654 0x0, 0x52, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x74, 0x0, 0x0, 0x0,
655 0x0, 0x0, 0x0, 0xf, 0xe5, 0x0, 0x46, 0x1, 0x0, 0x66, 0x0, 0x3,
656 0x0, 0xb, 0xf4, 0x0, 0x50, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x9,
657 0xff, 0x0, 0x50, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7, 0x8, 0x1,
658 0x50, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7, 0xf, 0x1, 0x44, 0x75,
659 0x6d, 0x6d, 0x79, 0x53, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x0, 0x43, 0x68,
660 0x69, 0x6c, 0x64, 0x41, 0x72, 0x72, 0x61, 0x79, 0x50, 0x72}};
661
662/**
663 * @brief MultipartReceive command with END flag set.
664 */
665constexpr std::array<uint8_t, 137> mRcvInput1End{
666 {0x00, 0x02, 0x02, 0x00, 0x00, 0x00, 0x7b, 0x00, 0x00, 0x00, 0x6f, 0x70,
667 0x65, 0x72, 0x74, 0x79, 0x0, 0x49, 0x64, 0x0, 0x53, 0x61, 0x6d, 0x70,
668 0x6c, 0x65, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x50, 0x72, 0x6f,
669 0x70, 0x65, 0x72, 0x74, 0x79, 0x0, 0x53, 0x61, 0x6d, 0x70, 0x6c, 0x65,
670 0x49, 0x6e, 0x74, 0x65, 0x67, 0x65, 0x72, 0x50, 0x72, 0x6f, 0x70, 0x65,
671 0x72, 0x74, 0x79, 0x0, 0x53, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x52, 0x65,
672 0x61, 0x6c, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 0x0, 0x41,
673 0x6e, 0x6f, 0x74, 0x68, 0x65, 0x72, 0x42, 0x6f, 0x6f, 0x6c, 0x65, 0x61,
674 0x6e, 0x0, 0x4c, 0x69, 0x6e, 0x6b, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73,
675 0x0, 0x4c, 0x69, 0x6e, 0x6b, 0x44, 0x6f, 0x77, 0x6e, 0x0, 0x4c, 0x69,
676 0x6e, 0x6b, 0x55, 0x70, 0x0, 0x4e, 0x6f, 0x4c, 0x69, 0x6e, 0x6b, 0x0,
677 0x0, 0x8c, 0x87, 0xed, 0x74}};
678
679/**
680 * @brief MultipartReceive command with START flag set.
681 */
682constexpr std::array<uint8_t, 106> mRcvInput2Start{
683 {0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x60, 0x0, 0x00, 0x00, 0x0, 0x0,
684 0xc, 0x0, 0x0, 0xf0, 0xf0, 0xf1, 0x17, 0x1, 0x0, 0x0, 0x0, 0x0,
685 0x0, 0x16, 0x0, 0x5, 0x0, 0xc, 0x84, 0x0, 0x14, 0x0, 0x0, 0x48,
686 0x0, 0x1, 0x0, 0x13, 0x90, 0x0, 0x56, 0x1, 0x0, 0x0, 0x0, 0x0,
687 0x0, 0x3, 0xa3, 0x0, 0x74, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x16,
688 0xa6, 0x0, 0x34, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x16, 0xbc, 0x0,
689 0x64, 0x4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x13, 0xd2, 0x0, 0x0, 0x0,
690 0x0, 0x52, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x74, 0x0, 0x0, 0x0,
691 0x0, 0x0, 0x0, 0xf, 0xe5, 0x0, 0x46, 0x1, 0x0, 0x66}};
692
693/**
694 * @brief MultipartReceive command with MIDDLE flag set.
695 */
696constexpr std::array<uint8_t, 106> mRcvInput2Mid{
697 {0x00, 0x01, 0x02, 0x00, 0x00, 0x00, 0x60, 0x0, 0x00, 0x00, 0x0, 0x3,
698 0x0, 0xb, 0xf4, 0x0, 0x50, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x9,
699 0xff, 0x0, 0x50, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7, 0x8, 0x1,
700 0x50, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7, 0xf, 0x1, 0x44, 0x75,
701 0x6d, 0x6d, 0x79, 0x53, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x0, 0x43, 0x68,
702 0x69, 0x6c, 0x64, 0x41, 0x72, 0x72, 0x61, 0x79, 0x50, 0x72, 0x6f, 0x70,
703 0x65, 0x72, 0x74, 0x79, 0x0, 0x49, 0x64, 0x0, 0x53, 0x61, 0x6d, 0x70,
704 0x6c, 0x65, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x50, 0x72, 0x6f,
705 0x70, 0x65, 0x72, 0x74, 0x79, 0x0, 0x53, 0x61, 0x6d, 0x70}};
706
707/**
708 * @brief MultipartReceive command with END flag set.
709 */
710constexpr std::array<uint8_t, 101> mRcvInput2End{
711 {0x00, 0x02, 0x02, 0x00, 0x00, 0x00, 0x57, 0x0, 0x00, 0x00, 0x6c, 0x65,
712 0x49, 0x6e, 0x74, 0x65, 0x67, 0x65, 0x72, 0x50, 0x72, 0x6f, 0x70, 0x65,
713 0x72, 0x74, 0x79, 0x0, 0x53, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x52, 0x65,
714 0x61, 0x6c, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 0x0, 0x41,
715 0x6e, 0x6f, 0x74, 0x68, 0x65, 0x72, 0x42, 0x6f, 0x6f, 0x6c, 0x65, 0x61,
716 0x6e, 0x0, 0x4c, 0x69, 0x6e, 0x6b, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73,
717 0x0, 0x4c, 0x69, 0x6e, 0x6b, 0x44, 0x6f, 0x77, 0x6e, 0x0, 0x4c, 0x69,
718 0x6e, 0x6b, 0x55, 0x70, 0x0, 0x4e, 0x6f, 0x4c, 0x69, 0x6e, 0x6b, 0x0,
719 0x0, 0x8c, 0x87, 0xed, 0x74}};
720
721/**
722 * @brief RDEOperationInit command with encoded json/dummysimple.json as the
723 * payload.
724 */
725constexpr std::array<uint8_t, 113> mInitOp{
726 {0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x02, 0x00, 0x00, 0x00, 0x00,
727 0x00, 0x60, 0x00, 0x00, 0x00, 0x0, 0xf0, 0xf0, 0xf1, 0x0, 0x0, 0x0,
728 0x1, 0x0, 0x0, 0x1, 0x54, 0x1, 0x5, 0x1, 0x2, 0x50, 0x1, 0x9,
729 0x44, 0x75, 0x6d, 0x6d, 0x79, 0x20, 0x49, 0x44, 0x0, 0x1, 0x6, 0x20,
730 0x1, 0x0, 0x1, 0x8, 0x60, 0x1, 0xb, 0x1, 0x2, 0x38, 0xea, 0x1,
731 0x0, 0x2, 0xa3, 0x23, 0x1, 0x0, 0x1, 0x4, 0x70, 0x1, 0x1, 0x0,
732 0x1, 0x0, 0x10, 0x1, 0x24, 0x1, 0x2, 0x1, 0x0, 0x0, 0x1, 0xf,
733 0x1, 0x2, 0x1, 0x0, 0x70, 0x1, 0x1, 0x1, 0x1, 0x2, 0x40, 0x1,
734 0x2, 0x1, 0x2, 0x1, 0x2, 0x0, 0x1, 0x9, 0x1, 0x1, 0x1, 0x2,
735 0x40, 0x1, 0x2, 0x1, 0x2}};
736
737class MockExternalStorer : public ExternalStorerInterface
738{
739 public:
740 MOCK_METHOD(bool, publishJson, (std::string_view jsonStr), (override));
741};
742
743class RdeHandlerTest : public ::testing::Test
744{
745 public:
746 RdeHandlerTest() : mockExStorer(std::make_unique<MockExternalStorer>())
747 {
748 mockExStorerPtr = dynamic_cast<MockExternalStorer*>(mockExStorer.get());
749 rdeH = std::make_unique<RdeCommandHandler>(std::move(mockExStorer));
750 }
751
752 protected:
753 std::unique_ptr<ExternalStorerInterface> mockExStorer;
754 std::unique_ptr<RdeCommandHandler> rdeH;
755 MockExternalStorer* mockExStorerPtr;
756 const std::string exJson =
757 R"({"Id":"Dummy ID","SampleIntegerProperty":null,"SampleRealProperty":-5576.9123,"SampleEnabledProperty":false,"ChildArrayProperty":[{"AnotherBoolean":true,"LinkStatus":"NoLink"},{"LinkStatus":"NoLink"}]})";
758};
759
760TEST_F(RdeHandlerTest, DictionaryStartAndEndTest)
761{
762 // Send a payload with START_AND_END flag.
763 EXPECT_THAT(
764 rdeH->decodeRdeCommand(std::span(mRcvInput0StartAndEnd),
765 RdeCommandType::RdeMultiPartReceiveResponse),
766 RdeDecodeStatus::RdeStopFlagReceived);
767 EXPECT_THAT(rdeH->getDictionaryCount(), 1);
768 // Send annotation dictionary.
769 EXPECT_THAT(
770 rdeH->decodeRdeCommand(std::span(mRcvDummyAnnotation),
771 RdeCommandType::RdeMultiPartReceiveResponse),
772 RdeDecodeStatus::RdeStopFlagReceived);
773 EXPECT_THAT(rdeH->getDictionaryCount(), 2);
774
775 // Send the encoded payload.
776 EXPECT_CALL(*mockExStorerPtr, publishJson(exJson)).WillOnce(Return(true));
777 EXPECT_THAT(rdeH->decodeRdeCommand(std::span(mInitOp),
778 RdeCommandType::RdeOperationInitRequest),
779 RdeDecodeStatus::RdeOk);
780}
781
782TEST_F(RdeHandlerTest, DictionaryStartThenEndTest)
783{
784 // Send a payload with START flag.
785 EXPECT_THAT(
786 rdeH->decodeRdeCommand(std::span(mRcvInput1Start),
787 RdeCommandType::RdeMultiPartReceiveResponse),
788 RdeDecodeStatus::RdeOk);
789 // We didn't send END. So dictionary count should be 0.
790 EXPECT_THAT(rdeH->getDictionaryCount(), 0);
791 // Send a payload with END flag.
792 EXPECT_THAT(
793 rdeH->decodeRdeCommand(std::span(mRcvInput1End),
794 RdeCommandType::RdeMultiPartReceiveResponse),
795 RdeDecodeStatus::RdeStopFlagReceived);
796 EXPECT_THAT(rdeH->getDictionaryCount(), 1);
797 // Send annotation dictionary.
798 EXPECT_THAT(
799 rdeH->decodeRdeCommand(std::span(mRcvDummyAnnotation),
800 RdeCommandType::RdeMultiPartReceiveResponse),
801 RdeDecodeStatus::RdeStopFlagReceived);
802 EXPECT_THAT(rdeH->getDictionaryCount(), 2);
803
804 // Send the encoded payload.
805 EXPECT_CALL(*mockExStorerPtr, publishJson(exJson)).WillOnce(Return(true));
806 EXPECT_THAT(rdeH->decodeRdeCommand(std::span(mInitOp),
807 RdeCommandType::RdeOperationInitRequest),
808 RdeDecodeStatus::RdeOk);
809
810 // Sending the START again for same resource ID should decrease the
811 // dictionary count.
812 EXPECT_THAT(
813 rdeH->decodeRdeCommand(std::span(mRcvInput1Start),
814 RdeCommandType::RdeMultiPartReceiveResponse),
815 RdeDecodeStatus::RdeOk);
816 EXPECT_THAT(rdeH->getDictionaryCount(), 1);
817}
818
819TEST_F(RdeHandlerTest, DictionaryStartMidEndTest)
820{
821 // Send a payload with START flag.
822 EXPECT_THAT(
823 rdeH->decodeRdeCommand(std::span(mRcvInput2Start),
824 RdeCommandType::RdeMultiPartReceiveResponse),
825 RdeDecodeStatus::RdeOk);
826 // We didn't send END. So dictionary count should be 0.
827 EXPECT_THAT(rdeH->getDictionaryCount(), 0);
828 // Send a payload with MIDDLE flag.
829 EXPECT_THAT(
830 rdeH->decodeRdeCommand(std::span(mRcvInput2Mid),
831 RdeCommandType::RdeMultiPartReceiveResponse),
832 RdeDecodeStatus::RdeOk);
833 // We didn't send END. So dictionary count should be 0.
834 EXPECT_THAT(rdeH->getDictionaryCount(), 0);
835 // Send a payload with END flag.
836 EXPECT_THAT(
837 rdeH->decodeRdeCommand(std::span(mRcvInput2End),
838 RdeCommandType::RdeMultiPartReceiveResponse),
839 RdeDecodeStatus::RdeStopFlagReceived);
840 EXPECT_THAT(rdeH->getDictionaryCount(), 1);
841
842 // Send annotation dictionary.
843 EXPECT_THAT(
844 rdeH->decodeRdeCommand(std::span(mRcvDummyAnnotation),
845 RdeCommandType::RdeMultiPartReceiveResponse),
846 RdeDecodeStatus::RdeStopFlagReceived);
847 EXPECT_THAT(rdeH->getDictionaryCount(), 2);
848
849 // Send the encoded payload.
850 EXPECT_CALL(*mockExStorerPtr, publishJson(exJson)).WillOnce(Return(true));
851 EXPECT_THAT(rdeH->decodeRdeCommand(std::span(mInitOp),
852 RdeCommandType::RdeOperationInitRequest),
853 RdeDecodeStatus::RdeOk);
854}
855
856TEST_F(RdeHandlerTest, InvalidDictionaryFlowTest)
857{
858 // Send a payload with MIDDLE flag before START and it should fail.
859 EXPECT_THAT(
860 rdeH->decodeRdeCommand(std::span(mRcvInput2Mid),
861 RdeCommandType::RdeMultiPartReceiveResponse),
862 RdeDecodeStatus::RdeInvalidPktOrder);
863 // Send a payload with END flag before START and it should fail.
864 EXPECT_THAT(
865 rdeH->decodeRdeCommand(std::span(mRcvInput2End),
866 RdeCommandType::RdeMultiPartReceiveResponse),
867 RdeDecodeStatus::RdeInvalidPktOrder);
868}
869
870TEST_F(RdeHandlerTest, MissingDictionaryTest)
871{
872 // Try decoding without any dictionaries.
873 EXPECT_THAT(rdeH->decodeRdeCommand(std::span(mInitOp),
874 RdeCommandType::RdeOperationInitRequest),
875 RdeDecodeStatus::RdeNoDictionary);
876
877 // Try decoding just with annotation dictionary.
878 EXPECT_THAT(
879 rdeH->decodeRdeCommand(std::span(mRcvDummyAnnotation),
880 RdeCommandType::RdeMultiPartReceiveResponse),
881 RdeDecodeStatus::RdeStopFlagReceived);
882 EXPECT_THAT(rdeH->decodeRdeCommand(std::span(mInitOp),
883 RdeCommandType::RdeOperationInitRequest),
884 RdeDecodeStatus::RdeNoDictionary);
885}
886
887TEST_F(RdeHandlerTest, InvalidDictionaryChecksumTest)
888{
889 // Send a dictionary with an invalid checksum.
890 EXPECT_THAT(
891 rdeH->decodeRdeCommand(std::span(mRcvDummyInvalidChecksum),
892 RdeCommandType::RdeMultiPartReceiveResponse),
893 RdeDecodeStatus::RdeInvalidChecksum);
894}
895
896} // namespace rde
897} // namespace bios_bmc_smm_error_logger