1 #include "fru_utils.hpp" 2 3 #include <algorithm> 4 #include <array> 5 #include <iterator> 6 7 #include "gtest/gtest.h" 8 9 extern "C" 10 { 11 // Include for I2C_SMBUS_BLOCK_MAX 12 #include <linux/i2c.h> 13 } 14 15 static constexpr size_t blockSize = I2C_SMBUS_BLOCK_MAX; 16 17 TEST(ValidateHeaderTest, InvalidFruVersionReturnsFalse) 18 { 19 // Validates the FruVersion is checked for the only legal value. 20 constexpr std::array<uint8_t, I2C_SMBUS_BLOCK_MAX> fruHeader = { 21 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; 22 23 EXPECT_FALSE(validateHeader(fruHeader)); 24 } 25 26 TEST(ValidateHeaderTest, InvalidReservedReturnsFalse) 27 { 28 // Validates the reserved bit(7:4) of first bytes. 29 constexpr std::array<uint8_t, I2C_SMBUS_BLOCK_MAX> fruHeader = { 30 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; 31 32 EXPECT_FALSE(validateHeader(fruHeader)); 33 } 34 35 TEST(ValidateHeaderTest, InvalidPaddingReturnsFalse) 36 { 37 // Validates the padding byte (7th byte). 38 constexpr std::array<uint8_t, I2C_SMBUS_BLOCK_MAX> fruHeader = { 39 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00}; 40 41 EXPECT_FALSE(validateHeader(fruHeader)); 42 } 43 44 TEST(ValidateHeaderTest, InvalidChecksumReturnsFalse) 45 { 46 // Validates the checksum, check for incorrect value. 47 constexpr std::array<uint8_t, I2C_SMBUS_BLOCK_MAX> fruHeader = { 48 0x01, 0x00, 0x01, 0x02, 0x03, 0x04, 0x00, 0x00}; 49 50 EXPECT_FALSE(validateHeader(fruHeader)); 51 } 52 53 TEST(ValidateHeaderTest, ValidChecksumReturnsTrue) 54 { 55 // Validates the checksum, check for correct value. 56 constexpr std::array<uint8_t, I2C_SMBUS_BLOCK_MAX> fruHeader = { 57 0x01, 0x00, 0x01, 0x02, 0x03, 0x04, 0x00, 0xf5}; 58 59 EXPECT_TRUE(validateHeader(fruHeader)); 60 } 61 62 TEST(VerifyOffsetTest, EmptyFruDataReturnsFalse) 63 { 64 // Validates the FruData size is checked for non empty. 65 std::vector<uint8_t> fruData = {}; 66 67 EXPECT_FALSE(verifyOffset(fruData, fruAreas::fruAreaChassis, 0)); 68 } 69 70 TEST(VerifyOffsetTest, AreaOutOfRangeReturnsFalse) 71 { 72 // Validates the FruArea value, check if it is within range. 73 const std::vector<uint8_t> fruData = {0x01, 0x00, 0x00, 0x00, 0x00, 74 0x00, 0x00, 0x00, 0x00}; 75 76 unsigned int areaOutOfRange = 8; 77 EXPECT_FALSE( 78 verifyOffset(fruData, static_cast<fruAreas>(areaOutOfRange), 0)); 79 } 80 81 TEST(VerifyOffsetTest, OverlapNextAreaReturnsFalse) 82 { 83 // Validates the Overlap of offsets with overlapped values. 84 const std::vector<uint8_t> fruData = {0x01, 0x00, 0x01, 0x02, 0x03, 85 0x04, 0x00, 0x00, 0x00}; 86 87 EXPECT_FALSE(verifyOffset(fruData, fruAreas::fruAreaChassis, 2)); 88 } 89 90 TEST(VerifyOffsetTest, OverlapPrevAreaReturnsFalse) 91 { 92 // Validates the Overlap of offsets with overlapped values. 93 const std::vector<uint8_t> fruData = {0x01, 0x00, 0x01, 0x03, 0x02, 94 0x07, 0x00, 0x00, 0x00}; 95 96 EXPECT_FALSE(verifyOffset(fruData, fruAreas::fruAreaProduct, 2)); 97 } 98 99 TEST(VerifyOffsetTest, ValidInputDataNoOverlapReturnsTrue) 100 { 101 // Validates all inputs with expected value and no overlap. 102 const std::vector<uint8_t> fruData = {0x01, 0x00, 0x01, 0x02, 0x03, 103 0x04, 0x00, 0x00, 0x00}; 104 105 EXPECT_TRUE(verifyOffset(fruData, fruAreas::fruAreaChassis, 1)); 106 } 107 108 TEST(VerifyChecksumTest, EmptyInput) 109 { 110 std::vector<uint8_t> data = {}; 111 112 EXPECT_EQ(calculateChecksum(data), 0); 113 } 114 115 TEST(VerifyChecksumTest, SingleOneInput) 116 { 117 std::vector<uint8_t> data(1, 1); 118 119 EXPECT_EQ(calculateChecksum(data), 255); 120 } 121 122 TEST(VerifyChecksumTest, AllOneInput) 123 { 124 std::vector<uint8_t> data(256, 1); 125 126 EXPECT_EQ(calculateChecksum(data), 0); 127 } 128 129 TEST(VerifyChecksumTest, WrapBoundaryLow) 130 { 131 std::vector<uint8_t> data = {255, 0}; 132 133 EXPECT_EQ(calculateChecksum(data), 1); 134 } 135 136 TEST(VerifyChecksumTest, WrapBoundaryExact) 137 { 138 std::vector<uint8_t> data = {255, 1}; 139 140 EXPECT_EQ(calculateChecksum(data), 0); 141 } 142 143 TEST(VerifyChecksumTest, WrapBoundaryHigh) 144 { 145 std::vector<uint8_t> data = {255, 2}; 146 147 EXPECT_EQ(calculateChecksum(data), 255); 148 } 149 150 int64_t getDataTempl(const std::vector<uint8_t>& data, off_t offset, 151 size_t length, uint8_t* outBuf) 152 { 153 if (offset >= static_cast<off_t>(data.size())) 154 { 155 return 0; 156 } 157 158 uint16_t idx = offset; 159 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) 160 for (; idx < std::min(data.size(), offset + length); ++idx, ++outBuf) 161 { 162 *outBuf = data[idx]; 163 } 164 165 return idx - offset; 166 } 167 168 TEST(FRUReaderTest, ReadData) 169 { 170 std::vector<uint8_t> data = {}; 171 for (size_t i = 0; i < blockSize * 2; i++) 172 { 173 data.push_back(i); 174 } 175 std::array<uint8_t, blockSize * 2> rdbuf{}; 176 auto getData = [&data](auto o, auto l, auto* b) { 177 return getDataTempl(data, o, l, b); 178 }; 179 FRUReader reader(getData); 180 181 EXPECT_EQ(reader.read(0, data.size(), rdbuf.data()), 182 static_cast<ssize_t>(data.size())); 183 EXPECT_TRUE(std::equal(rdbuf.begin(), rdbuf.end(), data.begin())); 184 for (size_t i = 0; i < blockSize * 2; i++) 185 { 186 EXPECT_EQ(reader.read(i, 1, rdbuf.data()), 1); 187 EXPECT_EQ(rdbuf[i], i); 188 } 189 EXPECT_EQ(reader.read(blockSize - 1, 2, rdbuf.data()), 2); 190 EXPECT_EQ(rdbuf[0], blockSize - 1); 191 EXPECT_EQ(rdbuf[1], blockSize); 192 } 193 194 TEST(FRUReaderTest, StartPastUnknownEOF) 195 { 196 const std::vector<uint8_t> data = {}; 197 auto getData = [&data](auto o, auto l, auto* b) { 198 return getDataTempl(data, o, l, b); 199 }; 200 FRUReader reader(getData); 201 202 EXPECT_EQ(reader.read(1, 1, nullptr), 0); 203 } 204 205 TEST(FRUReaderTest, StartPastKnownEOF) 206 { 207 std::vector<uint8_t> data = {}; 208 data.resize(blockSize / 2); 209 std::array<uint8_t, blockSize> blockData{}; 210 auto getData = [&data](auto o, auto l, auto* b) { 211 return getDataTempl(data, o, l, b); 212 }; 213 FRUReader reader(getData); 214 215 EXPECT_EQ(reader.read(0, blockSize, blockData.data()), 216 static_cast<ssize_t>(data.size())); 217 EXPECT_EQ(reader.read(data.size(), 1, nullptr), 0); 218 EXPECT_EQ(reader.read(data.size() + 1, 1, nullptr), 0); 219 EXPECT_EQ(reader.read(blockSize, 1, nullptr), 0); 220 EXPECT_EQ(reader.read(blockSize + 1, 1, nullptr), 0); 221 } 222 223 TEST(FRUReaderTest, DecreasingEOF) 224 { 225 const std::vector<uint8_t> data = {}; 226 auto getData = [&data](auto o, auto l, auto* b) { 227 return getDataTempl(data, o, l, b); 228 }; 229 FRUReader reader(getData); 230 231 EXPECT_EQ(reader.read(blockSize * 2, 1, nullptr), 0); 232 EXPECT_EQ(reader.read(blockSize + (blockSize / 2), 1, nullptr), 0); 233 EXPECT_EQ(reader.read(blockSize, 1, nullptr), 0); 234 EXPECT_EQ(reader.read(blockSize / 2, 1, nullptr), 0); 235 EXPECT_EQ(reader.read(0, 1, nullptr), 0); 236 } 237 238 TEST(FRUReaderTest, CacheHit) 239 { 240 std::vector<uint8_t> data = {'X'}; 241 std::array<uint8_t, blockSize> read1{}; 242 std::array<uint8_t, blockSize> read2{}; 243 auto getData = [&data](auto o, auto l, auto* b) { 244 return getDataTempl(data, o, l, b); 245 }; 246 FRUReader reader(getData); 247 248 // cache hit should return the same data for the second read even if we 249 // change it behind the FRUReader's back after the first 250 EXPECT_EQ(reader.read(0, blockSize, read1.data()), 1); 251 data[0] = 'Y'; 252 EXPECT_EQ(reader.read(0, blockSize, read2.data()), 1); 253 EXPECT_EQ(read1[0], read2[0]); 254 } 255 256 TEST(FRUReaderTest, ReadPastKnownEnd) 257 { 258 const std::vector<uint8_t> data = {'X', 'Y'}; 259 std::array<uint8_t, blockSize> rdbuf{}; 260 auto getData = [&data](auto o, auto l, auto* b) { 261 return getDataTempl(data, o, l, b); 262 }; 263 FRUReader reader(getData); 264 265 EXPECT_EQ(reader.read(0, data.size(), rdbuf.data()), 266 static_cast<ssize_t>(data.size())); 267 EXPECT_EQ(rdbuf[0], 'X'); 268 EXPECT_EQ(rdbuf[1], 'Y'); 269 EXPECT_EQ(reader.read(1, data.size(), rdbuf.data()), 270 static_cast<ssize_t>(data.size() - 1)); 271 EXPECT_EQ(rdbuf[0], 'Y'); 272 } 273 274 TEST(FRUReaderTest, MultiBlockRead) 275 { 276 std::vector<uint8_t> data = {}; 277 data.resize(blockSize, 'X'); 278 data.resize(2 * blockSize, 'Y'); 279 std::array<uint8_t, 2 * blockSize> rdbuf{}; 280 auto getData = [&data](auto o, auto l, auto* b) { 281 return getDataTempl(data, o, l, b); 282 }; 283 FRUReader reader(getData); 284 285 EXPECT_EQ(reader.read(0, 2 * blockSize, rdbuf.data()), 286 static_cast<ssize_t>(2 * blockSize)); 287 EXPECT_TRUE(std::equal(rdbuf.begin(), rdbuf.end(), data.begin())); 288 } 289 290 TEST(FRUReaderTest, ShrinkingEEPROM) 291 { 292 std::vector<uint8_t> data = {}; 293 data.resize(3 * blockSize, 'X'); 294 std::array<uint8_t, blockSize> rdbuf{}; 295 auto getData = [&data](auto o, auto l, auto* b) { 296 return getDataTempl(data, o, l, b); 297 }; 298 FRUReader reader(getData); 299 300 EXPECT_EQ(reader.read(data.size() - 1, 2, rdbuf.data()), 1); 301 data.resize(blockSize); 302 EXPECT_EQ(reader.read(data.size() - 1, 2, rdbuf.data()), 1); 303 } 304 305 TEST(FindFRUHeaderTest, InvalidHeader) 306 { 307 const std::vector<uint8_t> data = {255, 16}; 308 off_t offset = 0; 309 std::array<uint8_t, I2C_SMBUS_BLOCK_MAX> blockData{}; 310 auto getData = [&data](auto o, auto l, auto* b) { 311 return getDataTempl(data, o, l, b); 312 }; 313 FRUReader reader(getData); 314 315 EXPECT_FALSE(findFRUHeader(reader, "error", blockData, offset)); 316 } 317 318 TEST(FindFRUHeaderTest, NoData) 319 { 320 const std::vector<uint8_t> data = {}; 321 off_t offset = 0; 322 std::array<uint8_t, I2C_SMBUS_BLOCK_MAX> blockData{}; 323 auto getData = [&data](auto o, auto l, auto* b) { 324 return getDataTempl(data, o, l, b); 325 }; 326 FRUReader reader(getData); 327 328 EXPECT_FALSE(findFRUHeader(reader, "error", blockData, offset)); 329 } 330 331 TEST(FindFRUHeaderTest, ValidHeader) 332 { 333 const std::vector<uint8_t> data = {0x01, 0x00, 0x01, 0x02, 334 0x03, 0x04, 0x00, 0xf5}; 335 off_t offset = 0; 336 std::array<uint8_t, I2C_SMBUS_BLOCK_MAX> blockData{}; 337 auto getData = [&data](auto o, auto l, auto* b) { 338 return getDataTempl(data, o, l, b); 339 }; 340 FRUReader reader(getData); 341 342 EXPECT_TRUE(findFRUHeader(reader, "error", blockData, offset)); 343 EXPECT_EQ(0, offset); 344 } 345 346 TEST(FindFRUHeaderTest, TyanInvalidHeader) 347 { 348 std::vector<uint8_t> data = {'$', 'T', 'Y', 'A', 'N', '$', 0, 0}; 349 data.resize(0x6000 + I2C_SMBUS_BLOCK_MAX); 350 off_t offset = 0; 351 std::array<uint8_t, I2C_SMBUS_BLOCK_MAX> blockData{}; 352 auto getData = [&data](auto o, auto l, auto* b) { 353 return getDataTempl(data, o, l, b); 354 }; 355 FRUReader reader(getData); 356 357 EXPECT_FALSE(findFRUHeader(reader, "error", blockData, offset)); 358 } 359 360 TEST(FindFRUHeaderTest, TyanNoData) 361 { 362 const std::vector<uint8_t> data = {'$', 'T', 'Y', 'A', 'N', '$', 0, 0}; 363 off_t offset = 0; 364 std::array<uint8_t, I2C_SMBUS_BLOCK_MAX> blockData{}; 365 auto getData = [&data](auto o, auto l, auto* b) { 366 return getDataTempl(data, o, l, b); 367 }; 368 FRUReader reader(getData); 369 370 EXPECT_FALSE(findFRUHeader(reader, "error", blockData, offset)); 371 } 372 373 TEST(FindFRUHeaderTest, TyanValidHeader) 374 { 375 std::vector<uint8_t> data = {'$', 'T', 'Y', 'A', 'N', '$', 0, 0}; 376 data.resize(0x6000); 377 constexpr std::array<uint8_t, I2C_SMBUS_BLOCK_MAX> fruHeader = { 378 0x01, 0x00, 0x01, 0x02, 0x03, 0x04, 0x00, 0xf5}; 379 copy(fruHeader.begin(), fruHeader.end(), back_inserter(data)); 380 381 off_t offset = 0; 382 std::array<uint8_t, I2C_SMBUS_BLOCK_MAX> blockData{}; 383 auto getData = [&data](auto o, auto l, auto* b) { 384 return getDataTempl(data, o, l, b); 385 }; 386 FRUReader reader(getData); 387 388 EXPECT_TRUE(findFRUHeader(reader, "error", blockData, offset)); 389 EXPECT_EQ(0x6000, offset); 390 } 391