xref: /openbmc/entity-manager/test/test_fru-utils.cpp (revision a915884a6d909fc2bf12daa85cd9424f7df6320f)
1 #include "fru_device/fru_utils.hpp"
2 
3 #include <algorithm>
4 #include <array>
5 #include <iterator>
6 
7 #include "gmock/gmock.h"
8 #include "gtest/gtest.h"
9 
10 using ::testing::Pair;
11 using ::testing::UnorderedElementsAre;
12 
13 extern "C"
14 {
15 // Include for I2C_SMBUS_BLOCK_MAX
16 #include <linux/i2c.h>
17 }
18 
19 static constexpr size_t blockSize = I2C_SMBUS_BLOCK_MAX;
20 
21 TEST(ValidateHeaderTest, InvalidFruVersionReturnsFalse)
22 {
23     // Validates the FruVersion is checked for the only legal value.
24     constexpr std::array<uint8_t, I2C_SMBUS_BLOCK_MAX> fruHeader = {
25         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
26 
27     EXPECT_FALSE(validateHeader(fruHeader));
28 }
29 
30 TEST(ValidateHeaderTest, InvalidReservedReturnsFalse)
31 {
32     // Validates the reserved bit(7:4) of first bytes.
33     constexpr std::array<uint8_t, I2C_SMBUS_BLOCK_MAX> fruHeader = {
34         0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
35 
36     EXPECT_FALSE(validateHeader(fruHeader));
37 }
38 
39 TEST(ValidateHeaderTest, InvalidPaddingReturnsFalse)
40 {
41     // Validates the padding byte (7th byte).
42     constexpr std::array<uint8_t, I2C_SMBUS_BLOCK_MAX> fruHeader = {
43         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00};
44 
45     EXPECT_FALSE(validateHeader(fruHeader));
46 }
47 
48 TEST(ValidateHeaderTest, InvalidChecksumReturnsFalse)
49 {
50     // Validates the checksum, check for incorrect value.
51     constexpr std::array<uint8_t, I2C_SMBUS_BLOCK_MAX> fruHeader = {
52         0x01, 0x00, 0x01, 0x02, 0x03, 0x04, 0x00, 0x00};
53 
54     EXPECT_FALSE(validateHeader(fruHeader));
55 }
56 
57 TEST(ValidateHeaderTest, ValidChecksumReturnsTrue)
58 {
59     // Validates the checksum, check for correct value.
60     constexpr std::array<uint8_t, I2C_SMBUS_BLOCK_MAX> fruHeader = {
61         0x01, 0x00, 0x01, 0x02, 0x03, 0x04, 0x00, 0xf5};
62 
63     EXPECT_TRUE(validateHeader(fruHeader));
64 }
65 
66 TEST(VerifyOffsetTest, EmptyFruDataReturnsFalse)
67 {
68     // Validates the FruData size is checked for non empty.
69     std::vector<uint8_t> fruData = {};
70 
71     EXPECT_FALSE(verifyOffset(fruData, fruAreas::fruAreaChassis, 0));
72 }
73 
74 TEST(VerifyOffsetTest, AreaOutOfRangeReturnsFalse)
75 {
76     // Validates the FruArea value, check if it is within range.
77     const std::vector<uint8_t> fruData = {0x01, 0x00, 0x00, 0x00, 0x00,
78                                           0x00, 0x00, 0x00, 0x00};
79 
80     unsigned int areaOutOfRange = 8;
81     EXPECT_FALSE(
82         verifyOffset(fruData, static_cast<fruAreas>(areaOutOfRange), 0));
83 }
84 
85 TEST(VerifyOffsetTest, OverlapNextAreaReturnsFalse)
86 {
87     // Validates the Overlap of offsets with overlapped values.
88     const std::vector<uint8_t> fruData = {0x01, 0x00, 0x01, 0x02, 0x03,
89                                           0x04, 0x00, 0x00, 0x00};
90 
91     EXPECT_FALSE(verifyOffset(fruData, fruAreas::fruAreaChassis, 2));
92 }
93 
94 TEST(VerifyOffsetTest, OverlapPrevAreaReturnsFalse)
95 {
96     // Validates the Overlap of offsets with overlapped values.
97     const std::vector<uint8_t> fruData = {0x01, 0x00, 0x01, 0x03, 0x02,
98                                           0x07, 0x00, 0x00, 0x00};
99 
100     EXPECT_FALSE(verifyOffset(fruData, fruAreas::fruAreaProduct, 2));
101 }
102 
103 TEST(VerifyOffsetTest, ValidInputDataNoOverlapReturnsTrue)
104 {
105     // Validates all inputs with expected value and no overlap.
106     const std::vector<uint8_t> fruData = {0x01, 0x00, 0x01, 0x02, 0x03,
107                                           0x04, 0x00, 0x00, 0x00};
108 
109     EXPECT_TRUE(verifyOffset(fruData, fruAreas::fruAreaChassis, 1));
110 }
111 
112 TEST(VerifyChecksumTest, EmptyInput)
113 {
114     std::vector<uint8_t> data = {};
115 
116     EXPECT_EQ(calculateChecksum(data), 0);
117 }
118 
119 TEST(VerifyChecksumTest, SingleOneInput)
120 {
121     std::vector<uint8_t> data(1, 1);
122 
123     EXPECT_EQ(calculateChecksum(data), 255);
124 }
125 
126 TEST(VerifyChecksumTest, AllOneInput)
127 {
128     std::vector<uint8_t> data(256, 1);
129 
130     EXPECT_EQ(calculateChecksum(data), 0);
131 }
132 
133 TEST(VerifyChecksumTest, WrapBoundaryLow)
134 {
135     std::vector<uint8_t> data = {255, 0};
136 
137     EXPECT_EQ(calculateChecksum(data), 1);
138 }
139 
140 TEST(VerifyChecksumTest, WrapBoundaryExact)
141 {
142     std::vector<uint8_t> data = {255, 1};
143 
144     EXPECT_EQ(calculateChecksum(data), 0);
145 }
146 
147 TEST(VerifyChecksumTest, WrapBoundaryHigh)
148 {
149     std::vector<uint8_t> data = {255, 2};
150 
151     EXPECT_EQ(calculateChecksum(data), 255);
152 }
153 
154 int64_t getDataTempl(const std::vector<uint8_t>& data, off_t offset,
155                      size_t length, uint8_t* outBuf)
156 {
157     if (offset >= static_cast<off_t>(data.size()))
158     {
159         return 0;
160     }
161 
162     uint16_t idx = offset;
163     // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
164     for (; idx < std::min(data.size(), offset + length); ++idx, ++outBuf)
165     {
166         *outBuf = data[idx];
167     }
168 
169     return idx - offset;
170 }
171 
172 TEST(FRUReaderTest, ReadData)
173 {
174     std::vector<uint8_t> data = {};
175     data.reserve(blockSize * 2);
176     for (size_t i = 0; i < blockSize * 2; i++)
177     {
178         data.push_back(i);
179     }
180     std::array<uint8_t, blockSize * 2> rdbuf{};
181     auto getData = [&data](auto o, auto l, auto* b) {
182         return getDataTempl(data, o, l, b);
183     };
184     FRUReader reader(getData);
185 
186     EXPECT_EQ(reader.read(0, data.size(), rdbuf.data()),
187               static_cast<ssize_t>(data.size()));
188     EXPECT_TRUE(std::equal(rdbuf.begin(), rdbuf.end(), data.begin()));
189     for (size_t i = 0; i < blockSize * 2; i++)
190     {
191         EXPECT_EQ(reader.read(i, 1, rdbuf.data()), 1);
192         EXPECT_EQ(rdbuf[i], i);
193     }
194     EXPECT_EQ(reader.read(blockSize - 1, 2, rdbuf.data()), 2);
195     EXPECT_EQ(rdbuf[0], blockSize - 1);
196     EXPECT_EQ(rdbuf[1], blockSize);
197 }
198 
199 TEST(FRUReaderTest, StartPastUnknownEOF)
200 {
201     const std::vector<uint8_t> data = {};
202     auto getData = [&data](auto o, auto l, auto* b) {
203         return getDataTempl(data, o, l, b);
204     };
205     FRUReader reader(getData);
206 
207     EXPECT_EQ(reader.read(1, 1, nullptr), 0);
208 }
209 
210 TEST(FRUReaderTest, StartPastKnownEOF)
211 {
212     std::vector<uint8_t> data = {};
213     data.resize(blockSize / 2);
214     std::array<uint8_t, blockSize> blockData{};
215     auto getData = [&data](auto o, auto l, auto* b) {
216         return getDataTempl(data, o, l, b);
217     };
218     FRUReader reader(getData);
219 
220     EXPECT_EQ(reader.read(0, blockSize, blockData.data()),
221               static_cast<ssize_t>(data.size()));
222     EXPECT_EQ(reader.read(data.size(), 1, nullptr), 0);
223     EXPECT_EQ(reader.read(data.size() + 1, 1, nullptr), 0);
224     EXPECT_EQ(reader.read(blockSize, 1, nullptr), 0);
225     EXPECT_EQ(reader.read(blockSize + 1, 1, nullptr), 0);
226 }
227 
228 TEST(FRUReaderTest, DecreasingEOF)
229 {
230     const std::vector<uint8_t> data = {};
231     auto getData = [&data](auto o, auto l, auto* b) {
232         return getDataTempl(data, o, l, b);
233     };
234     FRUReader reader(getData);
235 
236     EXPECT_EQ(reader.read(blockSize * 2, 1, nullptr), 0);
237     EXPECT_EQ(reader.read(blockSize + (blockSize / 2), 1, nullptr), 0);
238     EXPECT_EQ(reader.read(blockSize, 1, nullptr), 0);
239     EXPECT_EQ(reader.read(blockSize / 2, 1, nullptr), 0);
240     EXPECT_EQ(reader.read(0, 1, nullptr), 0);
241 }
242 
243 TEST(FRUReaderTest, CacheHit)
244 {
245     std::vector<uint8_t> data = {'X'};
246     std::array<uint8_t, blockSize> read1{};
247     std::array<uint8_t, blockSize> read2{};
248     auto getData = [&data](auto o, auto l, auto* b) {
249         return getDataTempl(data, o, l, b);
250     };
251     FRUReader reader(getData);
252 
253     // cache hit should return the same data for the second read even if we
254     // change it behind the FRUReader's back after the first
255     EXPECT_EQ(reader.read(0, blockSize, read1.data()), 1);
256     data[0] = 'Y';
257     EXPECT_EQ(reader.read(0, blockSize, read2.data()), 1);
258     EXPECT_EQ(read1[0], read2[0]);
259 }
260 
261 TEST(FRUReaderTest, ReadPastKnownEnd)
262 {
263     const std::vector<uint8_t> data = {'X', 'Y'};
264     std::array<uint8_t, blockSize> rdbuf{};
265     auto getData = [&data](auto o, auto l, auto* b) {
266         return getDataTempl(data, o, l, b);
267     };
268     FRUReader reader(getData);
269 
270     EXPECT_EQ(reader.read(0, data.size(), rdbuf.data()),
271               static_cast<ssize_t>(data.size()));
272     EXPECT_EQ(rdbuf[0], 'X');
273     EXPECT_EQ(rdbuf[1], 'Y');
274     EXPECT_EQ(reader.read(1, data.size(), rdbuf.data()),
275               static_cast<ssize_t>(data.size() - 1));
276     EXPECT_EQ(rdbuf[0], 'Y');
277 }
278 
279 TEST(FRUReaderTest, MultiBlockRead)
280 {
281     std::vector<uint8_t> data = {};
282     data.resize(blockSize, 'X');
283     data.resize(2 * blockSize, 'Y');
284     std::array<uint8_t, 2 * blockSize> rdbuf{};
285     auto getData = [&data](auto o, auto l, auto* b) {
286         return getDataTempl(data, o, l, b);
287     };
288     FRUReader reader(getData);
289 
290     EXPECT_EQ(reader.read(0, 2 * blockSize, rdbuf.data()),
291               static_cast<ssize_t>(2 * blockSize));
292     EXPECT_TRUE(std::equal(rdbuf.begin(), rdbuf.end(), data.begin()));
293 }
294 
295 TEST(FRUReaderTest, ShrinkingEEPROM)
296 {
297     std::vector<uint8_t> data = {};
298     data.resize(3 * blockSize, 'X');
299     std::array<uint8_t, blockSize> rdbuf{};
300     auto getData = [&data](auto o, auto l, auto* b) {
301         return getDataTempl(data, o, l, b);
302     };
303     FRUReader reader(getData);
304 
305     EXPECT_EQ(reader.read(data.size() - 1, 2, rdbuf.data()), 1);
306     data.resize(blockSize);
307     EXPECT_EQ(reader.read(data.size() - 1, 2, rdbuf.data()), 1);
308 }
309 
310 TEST(FindFRUHeaderTest, InvalidHeader)
311 {
312     const std::vector<uint8_t> data = {255, 16};
313     off_t offset = 0;
314     std::array<uint8_t, I2C_SMBUS_BLOCK_MAX> blockData{};
315     auto getData = [&data](auto o, auto l, auto* b) {
316         return getDataTempl(data, o, l, b);
317     };
318     FRUReader reader(getData);
319 
320     EXPECT_FALSE(findFRUHeader(reader, "error", blockData, offset));
321 }
322 
323 TEST(FindFRUHeaderTest, NoData)
324 {
325     const std::vector<uint8_t> data = {};
326     off_t offset = 0;
327     std::array<uint8_t, I2C_SMBUS_BLOCK_MAX> blockData{};
328     auto getData = [&data](auto o, auto l, auto* b) {
329         return getDataTempl(data, o, l, b);
330     };
331     FRUReader reader(getData);
332 
333     EXPECT_FALSE(findFRUHeader(reader, "error", blockData, offset));
334 }
335 
336 TEST(FindFRUHeaderTest, ValidHeader)
337 {
338     const std::vector<uint8_t> data = {0x01, 0x00, 0x01, 0x02,
339                                        0x03, 0x04, 0x00, 0xf5};
340     off_t offset = 0;
341     std::array<uint8_t, I2C_SMBUS_BLOCK_MAX> blockData{};
342     auto getData = [&data](auto o, auto l, auto* b) {
343         return getDataTempl(data, o, l, b);
344     };
345     FRUReader reader(getData);
346 
347     EXPECT_TRUE(findFRUHeader(reader, "error", blockData, offset));
348     EXPECT_EQ(0, offset);
349 }
350 
351 TEST(FindFRUHeaderTest, TyanInvalidHeader)
352 {
353     std::vector<uint8_t> data = {'$', 'T', 'Y', 'A', 'N', '$', 0, 0};
354     data.resize(0x6000 + I2C_SMBUS_BLOCK_MAX);
355     off_t offset = 0;
356     std::array<uint8_t, I2C_SMBUS_BLOCK_MAX> blockData{};
357     auto getData = [&data](auto o, auto l, auto* b) {
358         return getDataTempl(data, o, l, b);
359     };
360     FRUReader reader(getData);
361 
362     EXPECT_FALSE(findFRUHeader(reader, "error", blockData, offset));
363 }
364 
365 TEST(FindFRUHeaderTest, TyanNoData)
366 {
367     const std::vector<uint8_t> data = {'$', 'T', 'Y', 'A', 'N', '$', 0, 0};
368     off_t offset = 0;
369     std::array<uint8_t, I2C_SMBUS_BLOCK_MAX> blockData{};
370     auto getData = [&data](auto o, auto l, auto* b) {
371         return getDataTempl(data, o, l, b);
372     };
373     FRUReader reader(getData);
374 
375     EXPECT_FALSE(findFRUHeader(reader, "error", blockData, offset));
376 }
377 
378 TEST(FindFRUHeaderTest, TyanValidHeader)
379 {
380     std::vector<uint8_t> data = {'$', 'T', 'Y', 'A', 'N', '$', 0, 0};
381     data.resize(0x6000);
382     constexpr std::array<uint8_t, I2C_SMBUS_BLOCK_MAX> fruHeader = {
383         0x01, 0x00, 0x01, 0x02, 0x03, 0x04, 0x00, 0xf5};
384     copy(fruHeader.begin(), fruHeader.end(), back_inserter(data));
385 
386     off_t offset = 0;
387     std::array<uint8_t, I2C_SMBUS_BLOCK_MAX> blockData{};
388     auto getData = [&data](auto o, auto l, auto* b) {
389         return getDataTempl(data, o, l, b);
390     };
391     FRUReader reader(getData);
392 
393     EXPECT_TRUE(findFRUHeader(reader, "error", blockData, offset));
394     EXPECT_EQ(0x6000, offset);
395 }
396 
397 TEST(formatIPMIFRU, FullDecode)
398 {
399     const std::array<uint8_t, 176> bmcFru = {
400         0x01, 0x00, 0x00, 0x01, 0x0b, 0x00, 0x00, 0xf3, 0x01, 0x0a, 0x19, 0x1f,
401         0x0f, 0xe6, 0xc6, 0x4e, 0x56, 0x49, 0x44, 0x49, 0x41, 0xc5, 0x50, 0x33,
402         0x38, 0x30, 0x39, 0xcd, 0x31, 0x35, 0x38, 0x33, 0x33, 0x32, 0x34, 0x38,
403         0x30, 0x30, 0x31, 0x35, 0x30, 0xd2, 0x36, 0x39, 0x39, 0x2d, 0x31, 0x33,
404         0x38, 0x30, 0x39, 0x2d, 0x30, 0x34, 0x30, 0x34, 0x2d, 0x36, 0x30, 0x30,
405         0xc0, 0x01, 0x01, 0xd6, 0x4d, 0x41, 0x43, 0x3a, 0x20, 0x33, 0x43, 0x3a,
406         0x36, 0x44, 0x3a, 0x36, 0x36, 0x3a, 0x31, 0x34, 0x3a, 0x43, 0x38, 0x3a,
407         0x37, 0x41, 0xc1, 0x3b, 0x01, 0x09, 0x19, 0xc6, 0x4e, 0x56, 0x49, 0x44,
408         0x49, 0x41, 0xc9, 0x50, 0x33, 0x38, 0x30, 0x39, 0x2d, 0x42, 0x4d, 0x43,
409         0xd2, 0x36, 0x39, 0x39, 0x2d, 0x31, 0x33, 0x38, 0x30, 0x39, 0x2d, 0x30,
410         0x34, 0x30, 0x34, 0x2d, 0x36, 0x30, 0x30, 0xc4, 0x41, 0x45, 0x2e, 0x31,
411         0xcd, 0x31, 0x35, 0x38, 0x33, 0x33, 0x32, 0x34, 0x38, 0x30, 0x30, 0x31,
412         0x35, 0x30, 0xc0, 0xc4, 0x76, 0x30, 0x2e, 0x31, 0xc1, 0x00, 0x00, 0x00,
413         0x00, 0x00, 0x00, 0xb4, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
414         0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
415     boost::container::flat_map<std::string, std::string> result;
416     ASSERT_EQ(formatIPMIFRU(bmcFru, result), resCodes::resOK);
417 
418     EXPECT_THAT(
419         result,
420         UnorderedElementsAre(
421             Pair("BOARD_FRU_VERSION_ID", ""), Pair("BOARD_INFO_AM1", "01"),
422             Pair("BOARD_INFO_AM2", "MAC: 3C:6D:66:14:C8:7A"),
423             Pair("BOARD_LANGUAGE_CODE", "25"),
424             Pair("BOARD_MANUFACTURER", "NVIDIA"),
425             Pair("BOARD_MANUFACTURE_DATE", "20240831T055100Z"),
426             Pair("BOARD_PART_NUMBER", "699-13809-0404-600"),
427             Pair("BOARD_PRODUCT_NAME", "P3809"),
428             Pair("BOARD_SERIAL_NUMBER", "1583324800150"),
429             Pair("Common_Format_Version", "1"), Pair("PRODUCT_ASSET_TAG", ""),
430             Pair("MAC_BOARD_INFO_AM2", "3C:6D:66:14:C8:7A"),
431             Pair("PRODUCT_FRU_VERSION_ID", "v0.1"),
432             Pair("PRODUCT_LANGUAGE_CODE", "25"),
433             Pair("PRODUCT_MANUFACTURER", "NVIDIA"),
434             Pair("PRODUCT_PART_NUMBER", "699-13809-0404-600"),
435             Pair("PRODUCT_PRODUCT_NAME", "P3809-BMC"),
436             Pair("PRODUCT_SERIAL_NUMBER", "1583324800150"),
437             Pair("PRODUCT_VERSION", "AE.1")));
438 }
439 
440 // Test for the `isFieldEditable` function
441 TEST(IsFieldEditableTest, ValidField)
442 {
443     // Test with a valid field name that is editable
444     // All PRODUCT fields are editable
445     EXPECT_TRUE(isFieldEditable("PRODUCT_MANUFACTURER"));
446     EXPECT_TRUE(isFieldEditable("PRODUCT_PART_NUMBER"));
447     EXPECT_TRUE(isFieldEditable("PRODUCT_SERIAL_NUMBER"));
448     EXPECT_TRUE(isFieldEditable("PRODUCT_VERSION"));
449     EXPECT_TRUE(isFieldEditable("PRODUCT_PART_NUMBER"));
450     EXPECT_TRUE(isFieldEditable("PRODUCT_PRODUCT_NAME"));
451     EXPECT_TRUE(isFieldEditable("PRODUCT_ASSET_TAG"));
452     EXPECT_TRUE(isFieldEditable("PRODUCT_FRU_VERSION_ID"));
453     EXPECT_TRUE(isFieldEditable("PRODUCT_INFO_AM0"));
454 
455     // All BOARD fields are editable
456     EXPECT_TRUE(isFieldEditable("BOARD_MANUFACTURER"));
457     EXPECT_TRUE(isFieldEditable("BOARD_PART_NUMBER"));
458     EXPECT_TRUE(isFieldEditable("BOARD_SERIAL_NUMBER"));
459     EXPECT_TRUE(isFieldEditable("BOARD_FRU_VERSION_ID"));
460     EXPECT_TRUE(isFieldEditable("BOARD_PRODUCT_NAME"));
461     EXPECT_TRUE(isFieldEditable("BOARD_INFO_AM0"));
462     EXPECT_TRUE(isFieldEditable("BOARD_INFO_AM10"));
463 
464     // All CHASSIS fields are editable
465     EXPECT_TRUE(isFieldEditable("CHASSIS_PART_NUMBER"));
466     EXPECT_TRUE(isFieldEditable("CHASSIS_SERIAL_NUMBER"));
467     EXPECT_TRUE(isFieldEditable("CHASSIS_INFO_AM0"));
468     EXPECT_TRUE(isFieldEditable("CHASSIS_INFO_AM10"));
469 }
470 
471 TEST(IsFieldEditableTest, InvalidField)
472 {
473     // Test with an invalid field name that is not editable
474     EXPECT_FALSE(isFieldEditable("INVALID_FIELD"));
475     EXPECT_FALSE(isFieldEditable("PRODUCT_INVALID_FIELD"));
476     EXPECT_FALSE(isFieldEditable("BOARD_INVALID_FIELD"));
477     EXPECT_FALSE(isFieldEditable("CHASSIS_INVALID_FIELD"));
478 
479     // Test with a field that does not match the expected pattern
480     EXPECT_FALSE(isFieldEditable("PRODUCT_12345"));
481     EXPECT_FALSE(isFieldEditable("BOARD_67890"));
482     EXPECT_FALSE(isFieldEditable("ABCD_CHASSIS"));
483     EXPECT_FALSE(isFieldEditable("ABCD_PRODUCT"));
484     EXPECT_FALSE(isFieldEditable("ABCD_BOARD"));
485 }
486 
487 TEST(UpdateAreaChecksumTest, EmptyArea)
488 {
489     // Validates that an empty area does not cause any issues.
490     std::vector<uint8_t> fruArea = {};
491     EXPECT_FALSE(updateAreaChecksum(fruArea));
492 }
493 
494 TEST(UpdateAreaChecksumTest, ValidArea)
495 {
496     // Validates that a valid area updates the checksum correctly.
497     std::vector<uint8_t> fruArea = {0x01, 0x00, 0x01, 0x02,
498                                     0x03, 0x04, 0x00, 0x00};
499     EXPECT_TRUE(updateAreaChecksum(fruArea));
500     EXPECT_EQ(fruArea.back(), 0xf5);
501 }
502 
503 TEST(UpdateAreaChecksumTest, InvalidArea)
504 {
505     // Validates that an invalid area does not update the checksum.
506     std::vector<uint8_t> fruArea = {0x01, 0x00, 0x01, 0x02, 0x03,
507                                     0x04, 0x00, 0x00, 0xAA};
508     EXPECT_FALSE(updateAreaChecksum(fruArea));
509 }
510 
511 TEST(DisassembleFruDataTest, EmptyData)
512 {
513     // Validates that an empty data vector returns false.
514     std::vector<uint8_t> fruData = {};
515     std::vector<std::vector<uint8_t>> areasData;
516     EXPECT_FALSE(disassembleFruData(fruData, areasData));
517 }
518 
519 TEST(DisassembleFruDataTest, ValidData)
520 {
521     // Taken from qemu fby35_bmc_fruid
522     std::vector<uint8_t> fruData = {
523         0x01, 0x00, 0x00, 0x01, 0x0d, 0x00, 0x00, 0xf1, 0x01, 0x0c, 0x00, 0x36,
524         0xe6, 0xd0, 0xc6, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0xd2, 0x42, 0x4d,
525         0x43, 0x20, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x20, 0x4d, 0x6f,
526         0x64, 0x75, 0x6c, 0x65, 0xcd, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58,
527         0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0xce, 0x58, 0x58, 0x58, 0x58, 0x58,
528         0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0xc3, 0x31, 0x2e,
529         0x30, 0xc9, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0xd2,
530         0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58,
531         0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0xc1, 0x39, 0x01, 0x0c, 0x00, 0xc6,
532         0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0xd2, 0x59, 0x6f, 0x73, 0x65, 0x6d,
533         0x69, 0x74, 0x65, 0x20, 0x56, 0x33, 0x2e, 0x35, 0x20, 0x45, 0x56, 0x54,
534         0x32, 0xce, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58,
535         0x58, 0x58, 0x58, 0x58, 0xc4, 0x45, 0x56, 0x54, 0x32, 0xcd, 0x58, 0x58,
536         0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0xc7,
537         0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0xc3, 0x31, 0x2e, 0x30, 0xc9,
538         0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0xc8, 0x43, 0x6f,
539         0x6e, 0x66, 0x69, 0x67, 0x20, 0x41, 0xc1, 0x45,
540     };
541 
542     std::vector<std::vector<uint8_t>> areasData;
543     ASSERT_TRUE(disassembleFruData(fruData, areasData));
544     EXPECT_GT(areasData.size(), 1U);
545 
546     // Internal area is size is zero
547     EXPECT_EQ(areasData[static_cast<size_t>(fruAreas::fruAreaInternal)].size(),
548               0U);
549     // Chassis are is zero
550     EXPECT_EQ(areasData[static_cast<size_t>(fruAreas::fruAreaChassis)].size(),
551               0U);
552     // Board area is 96 byte
553     EXPECT_EQ(areasData[static_cast<size_t>(fruAreas::fruAreaBoard)].size(),
554               96U);
555     // Product area is 96 byte
556     EXPECT_EQ(areasData[static_cast<size_t>(fruAreas::fruAreaProduct)].size(),
557               96U);
558 
559     // Multi-record area is 64 byte.
560     EXPECT_EQ(
561         areasData[static_cast<size_t>(fruAreas::fruAreaMultirecord)].size(),
562         0U);
563 
564     EXPECT_TRUE(setField(fruAreas::fruAreaBoard,
565                          areasData[static_cast<size_t>(fruAreas::fruAreaBoard)],
566                          "BOARD_INFO_AM1", "01"));
567     EXPECT_TRUE(setField(fruAreas::fruAreaBoard,
568                          areasData[static_cast<size_t>(fruAreas::fruAreaBoard)],
569                          "BOARD_INFO_AM2", "MAC: 3C:6D:66:14:C8:7A"));
570     // set Product fields
571     EXPECT_TRUE(
572         setField(fruAreas::fruAreaProduct,
573                  areasData[static_cast<size_t>(fruAreas::fruAreaProduct)],
574                  "PRODUCT_ASSET_TAG", "123"));
575     EXPECT_TRUE(
576         setField(fruAreas::fruAreaProduct,
577                  areasData[static_cast<size_t>(fruAreas::fruAreaProduct)],
578                  "PRODUCT_PART_NUMBER", "699-13809-0404-600"));
579     EXPECT_TRUE(
580         setField(fruAreas::fruAreaProduct,
581                  areasData[static_cast<size_t>(fruAreas::fruAreaProduct)],
582                  "PRODUCT_PRODUCT_NAME", "OpenBMC-test1"));
583 
584     EXPECT_EQ(
585         areasData[static_cast<size_t>(fruAreas::fruAreaProduct)].size() % 8, 0);
586     EXPECT_EQ(areasData[static_cast<size_t>(fruAreas::fruAreaBoard)].size() % 8,
587               0);
588 
589     std::vector<uint8_t> assembledData;
590     EXPECT_TRUE(assembleFruData(assembledData, areasData));
591 
592     boost::container::flat_map<std::string, std::string> result;
593     auto rescode = formatIPMIFRU(assembledData, result);
594     EXPECT_NE(rescode, resCodes::resErr);
595 
596     EXPECT_EQ(result["PRODUCT_ASSET_TAG"], "123");
597     EXPECT_EQ(result["PRODUCT_PART_NUMBER"], "699-13809-0404-600");
598     EXPECT_EQ(result["PRODUCT_PRODUCT_NAME"], "OpenBMC-test1");
599     EXPECT_EQ(result["BOARD_INFO_AM1"], "01");
600     EXPECT_EQ(result["BOARD_INFO_AM2"], "MAC: 3C:6D:66:14:C8:7A");
601 }
602 
603 TEST(ReassembleFruDataTest, UnalignedFails)
604 {
605     std::vector<uint8_t> areaOne{0, 35};
606     std::vector<uint8_t> areaTwo{0, 32};
607     std::vector<std::vector<uint8_t>> areas;
608     areas.push_back(areaOne);
609     areas.push_back(areaTwo);
610 
611     std::vector<uint8_t> fruData;
612     EXPECT_FALSE(assembleFruData(fruData, areas));
613 }
614