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