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(const std::vector<uint8_t> & data,off_t offset,size_t length,uint8_t * outBuf)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
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 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
TEST(FindFRUHeaderTest,NoData)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
TEST(FindFRUHeaderTest,ValidHeader)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
TEST(FindFRUHeaderTest,TyanInvalidHeader)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
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 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
TEST(FindFRUHeaderTest,TyanValidHeader)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
TEST(formatIPMIFRU,FullDecode)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
TEST(IsFieldEditableTest,ValidField)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
TEST(IsFieldEditableTest,InvalidField)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
TEST(UpdateAreaChecksumTest,EmptyArea)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
TEST(UpdateAreaChecksumTest,ValidArea)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
TEST(UpdateAreaChecksumTest,InvalidArea)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
TEST(DisassembleFruDataTest,EmptyData)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
TEST(DisassembleFruDataTest,ValidData)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
TEST(ReassembleFruDataTest,UnalignedFails)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