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; 159 for (idx = offset; idx < data.size() && idx < offset + length; 160 ++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, read2; 242 auto getData = [&data](auto o, auto l, auto* b) { 243 return getDataTempl(data, o, l, b); 244 }; 245 FRUReader reader(getData); 246 247 // cache hit should return the same data for the second read even if we 248 // change it behind the FRUReader's back after the first 249 EXPECT_EQ(reader.read(0, blockSize, read1.data()), 1); 250 data[0] = 'Y'; 251 EXPECT_EQ(reader.read(0, blockSize, read2.data()), 1); 252 EXPECT_EQ(read1[0], read2[0]); 253 } 254 255 TEST(FRUReaderTest, ReadPastKnownEnd) 256 { 257 const std::vector<uint8_t> data = {'X', 'Y'}; 258 std::array<uint8_t, blockSize> rdbuf; 259 auto getData = [&data](auto o, auto l, auto* b) { 260 return getDataTempl(data, o, l, b); 261 }; 262 FRUReader reader(getData); 263 264 EXPECT_EQ(reader.read(0, data.size(), rdbuf.data()), 265 static_cast<ssize_t>(data.size())); 266 EXPECT_EQ(rdbuf[0], 'X'); 267 EXPECT_EQ(rdbuf[1], 'Y'); 268 EXPECT_EQ(reader.read(1, data.size(), rdbuf.data()), 269 static_cast<ssize_t>(data.size() - 1)); 270 EXPECT_EQ(rdbuf[0], 'Y'); 271 } 272 273 TEST(FRUReaderTest, MultiBlockRead) 274 { 275 std::vector<uint8_t> data = {}; 276 data.resize(blockSize, 'X'); 277 data.resize(2 * blockSize, 'Y'); 278 std::array<uint8_t, 2 * blockSize> rdbuf; 279 auto getData = [&data](auto o, auto l, auto* b) { 280 return getDataTempl(data, o, l, b); 281 }; 282 FRUReader reader(getData); 283 284 EXPECT_EQ(reader.read(0, 2 * blockSize, rdbuf.data()), 285 static_cast<ssize_t>(2 * blockSize)); 286 EXPECT_TRUE(std::equal(rdbuf.begin(), rdbuf.end(), data.begin())); 287 } 288 289 TEST(FRUReaderTest, ShrinkingEEPROM) 290 { 291 std::vector<uint8_t> data = {}; 292 data.resize(3 * blockSize, 'X'); 293 std::array<uint8_t, blockSize> rdbuf; 294 auto getData = [&data](auto o, auto l, auto* b) { 295 return getDataTempl(data, o, l, b); 296 }; 297 FRUReader reader(getData); 298 299 EXPECT_EQ(reader.read(data.size() - 1, 2, rdbuf.data()), 1); 300 data.resize(blockSize); 301 EXPECT_EQ(reader.read(data.size() - 1, 2, rdbuf.data()), 1); 302 } 303 304 TEST(FindFRUHeaderTest, InvalidHeader) 305 { 306 const std::vector<uint8_t> data = {255, 16}; 307 off_t offset = 0; 308 std::array<uint8_t, I2C_SMBUS_BLOCK_MAX> blockData; 309 auto getData = [&data](auto o, auto l, auto* b) { 310 return getDataTempl(data, o, l, b); 311 }; 312 FRUReader reader(getData); 313 314 EXPECT_FALSE(findFRUHeader(reader, "error", blockData, offset)); 315 } 316 317 TEST(FindFRUHeaderTest, NoData) 318 { 319 const std::vector<uint8_t> data = {}; 320 off_t offset = 0; 321 std::array<uint8_t, I2C_SMBUS_BLOCK_MAX> blockData; 322 auto getData = [&data](auto o, auto l, auto* b) { 323 return getDataTempl(data, o, l, b); 324 }; 325 FRUReader reader(getData); 326 327 EXPECT_FALSE(findFRUHeader(reader, "error", blockData, offset)); 328 } 329 330 TEST(FindFRUHeaderTest, ValidHeader) 331 { 332 const std::vector<uint8_t> data = {0x01, 0x00, 0x01, 0x02, 333 0x03, 0x04, 0x00, 0xf5}; 334 off_t offset = 0; 335 std::array<uint8_t, I2C_SMBUS_BLOCK_MAX> blockData; 336 auto getData = [&data](auto o, auto l, auto* b) { 337 return getDataTempl(data, o, l, b); 338 }; 339 FRUReader reader(getData); 340 341 EXPECT_TRUE(findFRUHeader(reader, "error", blockData, offset)); 342 EXPECT_EQ(0, offset); 343 } 344 345 TEST(FindFRUHeaderTest, TyanInvalidHeader) 346 { 347 std::vector<uint8_t> data = {'$', 'T', 'Y', 'A', 'N', '$', 0, 0}; 348 data.resize(0x6000 + I2C_SMBUS_BLOCK_MAX); 349 off_t offset = 0; 350 std::array<uint8_t, I2C_SMBUS_BLOCK_MAX> blockData; 351 auto getData = [&data](auto o, auto l, auto* b) { 352 return getDataTempl(data, o, l, b); 353 }; 354 FRUReader reader(getData); 355 356 EXPECT_FALSE(findFRUHeader(reader, "error", blockData, offset)); 357 } 358 359 TEST(FindFRUHeaderTest, TyanNoData) 360 { 361 const std::vector<uint8_t> data = {'$', 'T', 'Y', 'A', 'N', '$', 0, 0}; 362 off_t offset = 0; 363 std::array<uint8_t, I2C_SMBUS_BLOCK_MAX> blockData; 364 auto getData = [&data](auto o, auto l, auto* b) { 365 return getDataTempl(data, o, l, b); 366 }; 367 FRUReader reader(getData); 368 369 EXPECT_FALSE(findFRUHeader(reader, "error", blockData, offset)); 370 } 371 372 TEST(FindFRUHeaderTest, TyanValidHeader) 373 { 374 std::vector<uint8_t> data = {'$', 'T', 'Y', 'A', 'N', '$', 0, 0}; 375 data.resize(0x6000); 376 constexpr std::array<uint8_t, I2C_SMBUS_BLOCK_MAX> fruHeader = { 377 0x01, 0x00, 0x01, 0x02, 0x03, 0x04, 0x00, 0xf5}; 378 copy(fruHeader.begin(), fruHeader.end(), back_inserter(data)); 379 380 off_t offset = 0; 381 std::array<uint8_t, I2C_SMBUS_BLOCK_MAX> blockData; 382 auto getData = [&data](auto o, auto l, auto* b) { 383 return getDataTempl(data, o, l, b); 384 }; 385 FRUReader reader(getData); 386 387 EXPECT_TRUE(findFRUHeader(reader, "error", blockData, offset)); 388 EXPECT_EQ(0x6000, offset); 389 } 390