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(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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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