xref: /openbmc/entity-manager/test/test_fru-utils.cpp (revision 10c57656f31ed1c20778425b6090cfae7d788ab6)
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 
TEST(ValidateHeaderTest,InvalidFruVersionReturnsFalse)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 
TEST(ValidateHeaderTest,InvalidReservedReturnsFalse)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 
TEST(ValidateHeaderTest,InvalidPaddingReturnsFalse)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 
TEST(ValidateHeaderTest,InvalidChecksumReturnsFalse)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 
TEST(ValidateHeaderTest,ValidChecksumReturnsTrue)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 
TEST(VerifyOffsetTest,EmptyFruDataReturnsFalse)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 
TEST(VerifyOffsetTest,AreaOutOfRangeReturnsFalse)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 
TEST(VerifyOffsetTest,OverlapNextAreaReturnsFalse)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 
TEST(VerifyOffsetTest,OverlapPrevAreaReturnsFalse)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 
TEST(VerifyOffsetTest,ValidInputDataNoOverlapReturnsTrue)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 
TEST(VerifyChecksumTest,EmptyInput)112 TEST(VerifyChecksumTest, EmptyInput)
113 {
114     std::vector<uint8_t> data = {};
115 
116     EXPECT_EQ(calculateChecksum(data), 0);
117 }
118 
TEST(VerifyChecksumTest,SingleOneInput)119 TEST(VerifyChecksumTest, SingleOneInput)
120 {
121     std::vector<uint8_t> data(1, 1);
122 
123     EXPECT_EQ(calculateChecksum(data), 255);
124 }
125 
TEST(VerifyChecksumTest,AllOneInput)126 TEST(VerifyChecksumTest, AllOneInput)
127 {
128     std::vector<uint8_t> data(256, 1);
129 
130     EXPECT_EQ(calculateChecksum(data), 0);
131 }
132 
TEST(VerifyChecksumTest,WrapBoundaryLow)133 TEST(VerifyChecksumTest, WrapBoundaryLow)
134 {
135     std::vector<uint8_t> data = {255, 0};
136 
137     EXPECT_EQ(calculateChecksum(data), 1);
138 }
139 
TEST(VerifyChecksumTest,WrapBoundaryExact)140 TEST(VerifyChecksumTest, WrapBoundaryExact)
141 {
142     std::vector<uint8_t> data = {255, 1};
143 
144     EXPECT_EQ(calculateChecksum(data), 0);
145 }
146 
TEST(VerifyChecksumTest,WrapBoundaryHigh)147 TEST(VerifyChecksumTest, WrapBoundaryHigh)
148 {
149     std::vector<uint8_t> data = {255, 2};
150 
151     EXPECT_EQ(calculateChecksum(data), 255);
152 }
153 
getDataTempl(std::span<const uint8_t> data,off_t offset,size_t length,uint8_t * outBuf)154 int64_t getDataTempl(std::span<const uint8_t> data, off_t offset, size_t length,
155                      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 
TEST(FRUReaderTest,ReadData)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 
TEST(FRUReaderTest,StartPastUnknownEOF)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 
TEST(FRUReaderTest,StartPastKnownEOF)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 
TEST(FRUReaderTest,DecreasingEOF)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 
TEST(FRUReaderTest,CacheHit)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 
TEST(FRUReaderTest,ReadPastKnownEnd)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 
TEST(FRUReaderTest,MultiBlockRead)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 
TEST(FRUReaderTest,ShrinkingEEPROM)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 
TEST(FindFRUHeaderTest,InvalidHeader)310 TEST(FindFRUHeaderTest, InvalidHeader)
311 {
312     const std::vector<uint8_t> data = {255, 16};
313     off_t offset = 0;
314     auto getData = [&data](auto o, auto l, auto* b) {
315         return getDataTempl(data, o, l, b);
316     };
317     FRUReader reader(getData);
318     auto sections = findFRUHeader(reader, "error", offset);
319     EXPECT_EQ(sections, std::nullopt);
320 }
321 
TEST(FindFRUHeaderTest,NoData)322 TEST(FindFRUHeaderTest, NoData)
323 {
324     const std::vector<uint8_t> data = {};
325     off_t offset = 0;
326     auto getData = [&data](auto o, auto l, auto* b) {
327         return getDataTempl(data, o, l, b);
328     };
329     FRUReader reader(getData);
330 
331     auto sections = findFRUHeader(reader, "error", offset);
332     EXPECT_EQ(sections, std::nullopt);
333 }
334 
TEST(FindFRUHeaderTest,ValidHeader)335 TEST(FindFRUHeaderTest, ValidHeader)
336 {
337     const std::vector<uint8_t> data = {0x01, 0x00, 0x01, 0x02,
338                                        0x03, 0x04, 0x00, 0xf5};
339     off_t offset = 0;
340     auto getData = [&data](auto o, auto l, auto* b) {
341         return getDataTempl(data, o, l, b);
342     };
343     FRUReader reader(getData);
344     auto sections = findFRUHeader(reader, "error", offset);
345     ASSERT_NE(sections, std::nullopt);
346     if (sections)
347     {
348         EXPECT_EQ(0, sections->IpmiFruOffset);
349     }
350 }
351 
TEST(FindFRUHeaderTest,TyanInvalidHeader)352 TEST(FindFRUHeaderTest, TyanInvalidHeader)
353 {
354     std::vector<uint8_t> data = {'$', 'T', 'Y', 'A', 'N', '$', 0, 0};
355     data.resize(0x6000 + I2C_SMBUS_BLOCK_MAX);
356     off_t offset = 0;
357     auto getData = [&data](auto o, auto l, auto* b) {
358         return getDataTempl(data, o, l, b);
359     };
360     FRUReader reader(getData);
361     auto sections = findFRUHeader(reader, "error", offset);
362     EXPECT_EQ(sections, std::nullopt);
363 }
364 
TEST(FindFRUHeaderTest,TyanNoData)365 TEST(FindFRUHeaderTest, TyanNoData)
366 {
367     const std::vector<uint8_t> data = {'$', 'T', 'Y', 'A', 'N', '$', 0, 0};
368     off_t offset = 0;
369     auto getData = [&data](auto o, auto l, auto* b) {
370         return getDataTempl(data, o, l, b);
371     };
372     FRUReader reader(getData);
373     auto sections = findFRUHeader(reader, "error", offset);
374     EXPECT_EQ(sections, std::nullopt);
375 }
376 
TEST(FindFRUHeaderTest,TyanValidHeader)377 TEST(FindFRUHeaderTest, TyanValidHeader)
378 {
379     std::vector<uint8_t> data = {'$', 'T', 'Y', 'A', 'N', '$', 0, 0};
380     data.resize(0x6000);
381     constexpr std::array<uint8_t, I2C_SMBUS_BLOCK_MAX> fruHeader = {
382         0x01, 0x00, 0x01, 0x02, 0x03, 0x04, 0x00, 0xf5};
383     copy(fruHeader.begin(), fruHeader.end(), back_inserter(data));
384 
385     off_t offset = 0;
386     auto getData = [&data](auto o, auto l, auto* b) {
387         return getDataTempl(data, o, l, b);
388     };
389     FRUReader reader(getData);
390 
391     auto sections = findFRUHeader(reader, "error", offset);
392 
393     ASSERT_NE(sections, std::nullopt);
394     if (sections)
395     {
396         EXPECT_EQ(0x6000, sections->IpmiFruOffset);
397     }
398 }
399 
TEST(FindFRUHeaderTest,GigaInvalidHeader)400 TEST(FindFRUHeaderTest, GigaInvalidHeader)
401 {
402     std::vector<uint8_t> data = {'G', 'I', 'G', 'A', 'B', 'Y', 'T', 'E'};
403     data.resize(0x6000 + I2C_SMBUS_BLOCK_MAX);
404     off_t offset = 0;
405     auto getData = [&data](auto o, auto l, auto* b) {
406         return getDataTempl(data, o, l, b);
407     };
408     FRUReader reader(getData);
409     auto sections = findFRUHeader(reader, "error", offset);
410     EXPECT_EQ(sections, std::nullopt);
411 }
412 
TEST(FindFRUHeaderTest,GigaNoData)413 TEST(FindFRUHeaderTest, GigaNoData)
414 {
415     const std::vector<uint8_t> data = {'G', 'I', 'G', 'A', 'B', 'Y', 'T', 'E'};
416     off_t offset = 0;
417     auto getData = [&data](auto o, auto l, auto* b) {
418         return getDataTempl(data, o, l, b);
419     };
420     FRUReader reader(getData);
421     auto sections = findFRUHeader(reader, "error", offset);
422     EXPECT_EQ(sections, std::nullopt);
423 }
424 
TEST(FindFRUHeaderTest,GigaValidHeader)425 TEST(FindFRUHeaderTest, GigaValidHeader)
426 {
427     std::vector<uint8_t> data = {'G', 'I', 'G', 'A', 'B', 'Y', 'T', 'E'};
428     data.resize(0x4000);
429     constexpr std::array<uint8_t, I2C_SMBUS_BLOCK_MAX> fruHeader = {
430         0x01, 0x00, 0x01, 0x02, 0x03, 0x04, 0x00, 0xf5};
431     copy(fruHeader.begin(), fruHeader.end(), back_inserter(data));
432 
433     off_t offset = 0;
434     auto getData = [&data](auto o, auto l, auto* b) {
435         return getDataTempl(data, o, l, b);
436     };
437     FRUReader reader(getData);
438 
439     auto sections = findFRUHeader(reader, "error", offset);
440 
441     ASSERT_NE(sections, std::nullopt);
442     if (sections)
443     {
444         EXPECT_EQ(0x4000, sections->IpmiFruOffset);
445         EXPECT_EQ(512, sections->GigabyteXmlOffset);
446     }
447 }
448 
TEST(formatIPMIFRU,FullDecode)449 TEST(formatIPMIFRU, FullDecode)
450 {
451     const std::array<uint8_t, 176> bmcFru = {
452         0x01, 0x00, 0x00, 0x01, 0x0b, 0x00, 0x00, 0xf3, 0x01, 0x0a, 0x19, 0x1f,
453         0x0f, 0xe6, 0xc6, 0x4e, 0x56, 0x49, 0x44, 0x49, 0x41, 0xc5, 0x50, 0x33,
454         0x38, 0x30, 0x39, 0xcd, 0x31, 0x35, 0x38, 0x33, 0x33, 0x32, 0x34, 0x38,
455         0x30, 0x30, 0x31, 0x35, 0x30, 0xd2, 0x36, 0x39, 0x39, 0x2d, 0x31, 0x33,
456         0x38, 0x30, 0x39, 0x2d, 0x30, 0x34, 0x30, 0x34, 0x2d, 0x36, 0x30, 0x30,
457         0xc0, 0x01, 0x01, 0xd6, 0x4d, 0x41, 0x43, 0x3a, 0x20, 0x33, 0x43, 0x3a,
458         0x36, 0x44, 0x3a, 0x36, 0x36, 0x3a, 0x31, 0x34, 0x3a, 0x43, 0x38, 0x3a,
459         0x37, 0x41, 0xc1, 0x3b, 0x01, 0x09, 0x19, 0xc6, 0x4e, 0x56, 0x49, 0x44,
460         0x49, 0x41, 0xc9, 0x50, 0x33, 0x38, 0x30, 0x39, 0x2d, 0x42, 0x4d, 0x43,
461         0xd2, 0x36, 0x39, 0x39, 0x2d, 0x31, 0x33, 0x38, 0x30, 0x39, 0x2d, 0x30,
462         0x34, 0x30, 0x34, 0x2d, 0x36, 0x30, 0x30, 0xc4, 0x41, 0x45, 0x2e, 0x31,
463         0xcd, 0x31, 0x35, 0x38, 0x33, 0x33, 0x32, 0x34, 0x38, 0x30, 0x30, 0x31,
464         0x35, 0x30, 0xc0, 0xc4, 0x76, 0x30, 0x2e, 0x31, 0xc1, 0x00, 0x00, 0x00,
465         0x00, 0x00, 0x00, 0xb4, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
466         0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
467     boost::container::flat_map<std::string, std::string> result;
468     ASSERT_EQ(formatIPMIFRU(bmcFru, result), resCodes::resOK);
469 
470     EXPECT_THAT(
471         result,
472         UnorderedElementsAre(
473             Pair("BOARD_FRU_VERSION_ID", ""), Pair("BOARD_INFO_AM1", "01"),
474             Pair("BOARD_INFO_AM2", "MAC: 3C:6D:66:14:C8:7A"),
475             Pair("BOARD_LANGUAGE_CODE", "25"),
476             Pair("BOARD_MANUFACTURER", "NVIDIA"),
477             Pair("BOARD_MANUFACTURE_DATE", "20240831T055100Z"),
478             Pair("BOARD_PART_NUMBER", "699-13809-0404-600"),
479             Pair("BOARD_PRODUCT_NAME", "P3809"),
480             Pair("BOARD_SERIAL_NUMBER", "1583324800150"),
481             Pair("Common_Format_Version", "1"), Pair("PRODUCT_ASSET_TAG", ""),
482             Pair("MAC_BOARD_INFO_AM2", "3C:6D:66:14:C8:7A"),
483             Pair("PRODUCT_FRU_VERSION_ID", "v0.1"),
484             Pair("PRODUCT_LANGUAGE_CODE", "25"),
485             Pair("PRODUCT_MANUFACTURER", "NVIDIA"),
486             Pair("PRODUCT_PART_NUMBER", "699-13809-0404-600"),
487             Pair("PRODUCT_PRODUCT_NAME", "P3809-BMC"),
488             Pair("PRODUCT_SERIAL_NUMBER", "1583324800150"),
489             Pair("PRODUCT_VERSION", "AE.1")));
490 }
491 
492 // Test for the `isFieldEditable` function
TEST(IsFieldEditableTest,ValidField)493 TEST(IsFieldEditableTest, ValidField)
494 {
495     // Test with a valid field name that is editable
496     // All PRODUCT fields are editable
497     EXPECT_TRUE(isFieldEditable("PRODUCT_MANUFACTURER"));
498     EXPECT_TRUE(isFieldEditable("PRODUCT_PART_NUMBER"));
499     EXPECT_TRUE(isFieldEditable("PRODUCT_SERIAL_NUMBER"));
500     EXPECT_TRUE(isFieldEditable("PRODUCT_VERSION"));
501     EXPECT_TRUE(isFieldEditable("PRODUCT_PART_NUMBER"));
502     EXPECT_TRUE(isFieldEditable("PRODUCT_PRODUCT_NAME"));
503     EXPECT_TRUE(isFieldEditable("PRODUCT_ASSET_TAG"));
504     EXPECT_TRUE(isFieldEditable("PRODUCT_FRU_VERSION_ID"));
505     EXPECT_TRUE(isFieldEditable("PRODUCT_INFO_AM0"));
506 
507     // All BOARD fields are editable
508     EXPECT_TRUE(isFieldEditable("BOARD_MANUFACTURER"));
509     EXPECT_TRUE(isFieldEditable("BOARD_PART_NUMBER"));
510     EXPECT_TRUE(isFieldEditable("BOARD_SERIAL_NUMBER"));
511     EXPECT_TRUE(isFieldEditable("BOARD_FRU_VERSION_ID"));
512     EXPECT_TRUE(isFieldEditable("BOARD_PRODUCT_NAME"));
513     EXPECT_TRUE(isFieldEditable("BOARD_INFO_AM0"));
514     EXPECT_TRUE(isFieldEditable("BOARD_INFO_AM10"));
515 
516     // All CHASSIS fields are editable
517     EXPECT_TRUE(isFieldEditable("CHASSIS_PART_NUMBER"));
518     EXPECT_TRUE(isFieldEditable("CHASSIS_SERIAL_NUMBER"));
519     EXPECT_TRUE(isFieldEditable("CHASSIS_INFO_AM0"));
520     EXPECT_TRUE(isFieldEditable("CHASSIS_INFO_AM10"));
521 }
522 
TEST(IsFieldEditableTest,InvalidField)523 TEST(IsFieldEditableTest, InvalidField)
524 {
525     // Test with an invalid field name that is not editable
526     EXPECT_FALSE(isFieldEditable("INVALID_FIELD"));
527     EXPECT_FALSE(isFieldEditable("PRODUCT_INVALID_FIELD"));
528     EXPECT_FALSE(isFieldEditable("BOARD_INVALID_FIELD"));
529     EXPECT_FALSE(isFieldEditable("CHASSIS_INVALID_FIELD"));
530 
531     // Test with a field that does not match the expected pattern
532     EXPECT_FALSE(isFieldEditable("PRODUCT_12345"));
533     EXPECT_FALSE(isFieldEditable("BOARD_67890"));
534     EXPECT_FALSE(isFieldEditable("ABCD_CHASSIS"));
535     EXPECT_FALSE(isFieldEditable("ABCD_PRODUCT"));
536     EXPECT_FALSE(isFieldEditable("ABCD_BOARD"));
537 }
538 
TEST(UpdateAreaChecksumTest,EmptyArea)539 TEST(UpdateAreaChecksumTest, EmptyArea)
540 {
541     // Validates that an empty area does not cause any issues.
542     std::vector<uint8_t> fruArea = {};
543     EXPECT_FALSE(updateAreaChecksum(fruArea));
544 }
545 
TEST(UpdateAreaChecksumTest,ValidArea)546 TEST(UpdateAreaChecksumTest, ValidArea)
547 {
548     // Validates that a valid area updates the checksum correctly.
549     std::vector<uint8_t> fruArea = {0x01, 0x00, 0x01, 0x02,
550                                     0x03, 0x04, 0x00, 0x00};
551     EXPECT_TRUE(updateAreaChecksum(fruArea));
552     EXPECT_EQ(fruArea.back(), 0xf5);
553 }
554 
TEST(UpdateAreaChecksumTest,InvalidArea)555 TEST(UpdateAreaChecksumTest, InvalidArea)
556 {
557     // Validates that an invalid area does not update the checksum.
558     std::vector<uint8_t> fruArea = {0x01, 0x00, 0x01, 0x02, 0x03,
559                                     0x04, 0x00, 0x00, 0xAA};
560     EXPECT_FALSE(updateAreaChecksum(fruArea));
561 }
562 
TEST(DisassembleFruDataTest,EmptyData)563 TEST(DisassembleFruDataTest, EmptyData)
564 {
565     // Validates that an empty data vector returns false.
566     std::vector<uint8_t> fruData = {};
567     std::vector<std::vector<uint8_t>> areasData;
568     EXPECT_FALSE(disassembleFruData(fruData, areasData));
569 }
570 
TEST(DisassembleFruDataTest,ValidData)571 TEST(DisassembleFruDataTest, ValidData)
572 {
573     // Taken from qemu fby35_bmc_fruid
574     std::vector<uint8_t> fruData = {
575         0x01, 0x00, 0x00, 0x01, 0x0d, 0x00, 0x00, 0xf1, 0x01, 0x0c, 0x00, 0x36,
576         0xe6, 0xd0, 0xc6, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0xd2, 0x42, 0x4d,
577         0x43, 0x20, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x20, 0x4d, 0x6f,
578         0x64, 0x75, 0x6c, 0x65, 0xcd, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58,
579         0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0xce, 0x58, 0x58, 0x58, 0x58, 0x58,
580         0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0xc3, 0x31, 0x2e,
581         0x30, 0xc9, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0xd2,
582         0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58,
583         0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0xc1, 0x39, 0x01, 0x0c, 0x00, 0xc6,
584         0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0xd2, 0x59, 0x6f, 0x73, 0x65, 0x6d,
585         0x69, 0x74, 0x65, 0x20, 0x56, 0x33, 0x2e, 0x35, 0x20, 0x45, 0x56, 0x54,
586         0x32, 0xce, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58,
587         0x58, 0x58, 0x58, 0x58, 0xc4, 0x45, 0x56, 0x54, 0x32, 0xcd, 0x58, 0x58,
588         0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0xc7,
589         0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0xc3, 0x31, 0x2e, 0x30, 0xc9,
590         0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0xc8, 0x43, 0x6f,
591         0x6e, 0x66, 0x69, 0x67, 0x20, 0x41, 0xc1, 0x45,
592     };
593 
594     std::vector<std::vector<uint8_t>> areasData;
595     ASSERT_TRUE(disassembleFruData(fruData, areasData));
596     EXPECT_GT(areasData.size(), 1U);
597 
598     // Internal area is size is zero
599     EXPECT_EQ(areasData[static_cast<size_t>(fruAreas::fruAreaInternal)].size(),
600               0U);
601     // Chassis are is zero
602     EXPECT_EQ(areasData[static_cast<size_t>(fruAreas::fruAreaChassis)].size(),
603               0U);
604     // Board area is 96 byte
605     EXPECT_EQ(areasData[static_cast<size_t>(fruAreas::fruAreaBoard)].size(),
606               96U);
607     // Product area is 96 byte
608     EXPECT_EQ(areasData[static_cast<size_t>(fruAreas::fruAreaProduct)].size(),
609               96U);
610 
611     // Multi-record area is 64 byte.
612     EXPECT_EQ(
613         areasData[static_cast<size_t>(fruAreas::fruAreaMultirecord)].size(),
614         0U);
615 
616     EXPECT_TRUE(setField(fruAreas::fruAreaBoard,
617                          areasData[static_cast<size_t>(fruAreas::fruAreaBoard)],
618                          "BOARD_INFO_AM1", "01"));
619     EXPECT_TRUE(setField(fruAreas::fruAreaBoard,
620                          areasData[static_cast<size_t>(fruAreas::fruAreaBoard)],
621                          "BOARD_INFO_AM2", "MAC: 3C:6D:66:14:C8:7A"));
622     // set Product fields
623     EXPECT_TRUE(
624         setField(fruAreas::fruAreaProduct,
625                  areasData[static_cast<size_t>(fruAreas::fruAreaProduct)],
626                  "PRODUCT_ASSET_TAG", "123"));
627     EXPECT_TRUE(
628         setField(fruAreas::fruAreaProduct,
629                  areasData[static_cast<size_t>(fruAreas::fruAreaProduct)],
630                  "PRODUCT_PART_NUMBER", "699-13809-0404-600"));
631     EXPECT_TRUE(
632         setField(fruAreas::fruAreaProduct,
633                  areasData[static_cast<size_t>(fruAreas::fruAreaProduct)],
634                  "PRODUCT_PRODUCT_NAME", "OpenBMC-test1"));
635 
636     EXPECT_EQ(
637         areasData[static_cast<size_t>(fruAreas::fruAreaProduct)].size() % 8, 0);
638     EXPECT_EQ(areasData[static_cast<size_t>(fruAreas::fruAreaBoard)].size() % 8,
639               0);
640 
641     std::vector<uint8_t> assembledData;
642     EXPECT_TRUE(assembleFruData(assembledData, areasData));
643 
644     boost::container::flat_map<std::string, std::string> result;
645     auto rescode = formatIPMIFRU(assembledData, result);
646     EXPECT_NE(rescode, resCodes::resErr);
647 
648     EXPECT_EQ(result["PRODUCT_ASSET_TAG"], "123");
649     EXPECT_EQ(result["PRODUCT_PART_NUMBER"], "699-13809-0404-600");
650     EXPECT_EQ(result["PRODUCT_PRODUCT_NAME"], "OpenBMC-test1");
651     EXPECT_EQ(result["BOARD_INFO_AM1"], "01");
652     EXPECT_EQ(result["BOARD_INFO_AM2"], "MAC: 3C:6D:66:14:C8:7A");
653 }
654 
TEST(ReassembleFruDataTest,UnalignedFails)655 TEST(ReassembleFruDataTest, UnalignedFails)
656 {
657     std::vector<uint8_t> areaOne{0, 35};
658     std::vector<uint8_t> areaTwo{0, 32};
659     std::vector<std::vector<uint8_t>> areas;
660     areas.push_back(areaOne);
661     areas.push_back(areaTwo);
662 
663     std::vector<uint8_t> fruData;
664     EXPECT_FALSE(assembleFruData(fruData, areas));
665 }
666 
667 constexpr auto gzip = std::to_array<uint8_t>(
668     {0x1f, 0x8b, 0x08, 0x08, 0x74, 0x47, 0xe4, 0x68, 0x00, 0x03, 0x66, 0x72,
669      0x75, 0x2e, 0x62, 0x69, 0x6e, 0x00, 0x9d, 0x91, 0xdf, 0x0a, 0x82, 0x30,
670      0x18, 0xc5, 0xef, 0x7d, 0x8a, 0xe1, 0x7d, 0x4d, 0xad, 0x0b, 0x1b, 0x73,
671      0x52, 0x9a, 0x21, 0x51, 0x37, 0xcb, 0x07, 0x18, 0x6e, 0xea, 0xa0, 0x36,
672      0x58, 0x12, 0x3d, 0x7e, 0x29, 0x5a, 0x5a, 0x08, 0xd1, 0xdd, 0x7e, 0xdf,
673      0x9f, 0x9d, 0xc3, 0xf9, 0x70, 0x78, 0xbf, 0x9c, 0xc1, 0x4d, 0x98, 0xab,
674      0xd4, 0x2a, 0xb0, 0xdd, 0xb9, 0x63, 0x03, 0xa1, 0x72, 0xcd, 0xa5, 0x2a,
675      0x03, 0x3b, 0x3b, 0x25, 0x33, 0xdf, 0x0e, 0x89, 0x85, 0x77, 0x94, 0xee,
676      0x33, 0x62, 0x01, 0x00, 0xf0, 0x46, 0x33, 0xc3, 0x53, 0x55, 0xe8, 0x16,
677      0x9b, 0xca, 0x81, 0x49, 0xd5, 0x43, 0xc3, 0xc7, 0x34, 0x1a, 0x60, 0x53,
678      0x49, 0x55, 0x2d, 0x4c, 0xc1, 0x72, 0xe1, 0x8e, 0x1b, 0xed, 0xb6, 0xe6,
679      0x82, 0xc4, 0x82, 0xcb, 0x9c, 0xd5, 0x82, 0x63, 0xd8, 0xf2, 0xf7, 0x14,
680      0xcb, 0xd7, 0x9c, 0x1b, 0x87, 0xb8, 0x0e, 0x4a, 0x12, 0xb4, 0x75, 0xd0,
681      0x62, 0x85, 0xfc, 0x25, 0x8a, 0xa3, 0xe7, 0x46, 0xdf, 0x1b, 0x8b, 0xc2,
682      0x29, 0xd5, 0xb7, 0x1d, 0x6f, 0xc2, 0x0e, 0xad, 0x98, 0xf9, 0xc3, 0x4b,
683      0xfc, 0x83, 0x97, 0x0f, 0x49, 0x4c, 0x6b, 0x23, 0x54, 0x59, 0x57, 0xc4,
684      0xc3, 0xf0, 0xf5, 0x1e, 0x84, 0x09, 0x07, 0x69, 0x36, 0xdf, 0x77, 0x51,
685      0x63, 0x38, 0xb8, 0x03, 0x86, 0xdd, 0x7d, 0x1e, 0x15, 0xc1, 0xa2, 0x29,
686      0xcf, 0x01, 0x00, 0x00});
687 
TEST(GzipUtils,parseMacFromGzipXmlHeader)688 TEST(GzipUtils, parseMacFromGzipXmlHeader)
689 {
690     FRUReader reader(std::bind_front(getDataTempl, gzip));
691 
692     std::string mac = parseMacFromGzipXmlHeader(reader, 0);
693     EXPECT_EQ(mac, "10:FF:E0:39:84:DC");
694 }
695