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
TEST(ValidateHeaderTest,InvalidFruVersionReturnsFalse)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
TEST(ValidateHeaderTest,InvalidReservedReturnsFalse)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
TEST(ValidateHeaderTest,InvalidPaddingReturnsFalse)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
TEST(ValidateHeaderTest,InvalidChecksumReturnsFalse)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
TEST(ValidateHeaderTest,ValidChecksumReturnsTrue)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
TEST(VerifyOffsetTest,EmptyFruDataReturnsFalse)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
TEST(VerifyOffsetTest,AreaOutOfRangeReturnsFalse)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
TEST(VerifyOffsetTest,OverlapNextAreaReturnsFalse)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
TEST(VerifyOffsetTest,OverlapPrevAreaReturnsFalse)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
TEST(VerifyOffsetTest,ValidInputDataNoOverlapReturnsTrue)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
TEST(VerifyChecksumTest,EmptyInput)108 TEST(VerifyChecksumTest, EmptyInput)
109 {
110 std::vector<uint8_t> data = {};
111
112 EXPECT_EQ(calculateChecksum(data), 0);
113 }
114
TEST(VerifyChecksumTest,SingleOneInput)115 TEST(VerifyChecksumTest, SingleOneInput)
116 {
117 std::vector<uint8_t> data(1, 1);
118
119 EXPECT_EQ(calculateChecksum(data), 255);
120 }
121
TEST(VerifyChecksumTest,AllOneInput)122 TEST(VerifyChecksumTest, AllOneInput)
123 {
124 std::vector<uint8_t> data(256, 1);
125
126 EXPECT_EQ(calculateChecksum(data), 0);
127 }
128
TEST(VerifyChecksumTest,WrapBoundaryLow)129 TEST(VerifyChecksumTest, WrapBoundaryLow)
130 {
131 std::vector<uint8_t> data = {255, 0};
132
133 EXPECT_EQ(calculateChecksum(data), 1);
134 }
135
TEST(VerifyChecksumTest,WrapBoundaryExact)136 TEST(VerifyChecksumTest, WrapBoundaryExact)
137 {
138 std::vector<uint8_t> data = {255, 1};
139
140 EXPECT_EQ(calculateChecksum(data), 0);
141 }
142
TEST(VerifyChecksumTest,WrapBoundaryHigh)143 TEST(VerifyChecksumTest, WrapBoundaryHigh)
144 {
145 std::vector<uint8_t> data = {255, 2};
146
147 EXPECT_EQ(calculateChecksum(data), 255);
148 }
149
getDataTempl(const std::vector<uint8_t> & data,off_t offset,size_t length,uint8_t * outBuf)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
TEST(FRUReaderTest,ReadData)168 TEST(FRUReaderTest, ReadData)
169 {
170 std::vector<uint8_t> data = {};
171 data.reserve(blockSize * 2);
172 for (size_t i = 0; i < blockSize * 2; i++)
173 {
174 data.push_back(i);
175 }
176 std::array<uint8_t, blockSize * 2> rdbuf{};
177 auto getData = [&data](auto o, auto l, auto* b) {
178 return getDataTempl(data, o, l, b);
179 };
180 FRUReader reader(getData);
181
182 EXPECT_EQ(reader.read(0, data.size(), rdbuf.data()),
183 static_cast<ssize_t>(data.size()));
184 EXPECT_TRUE(std::equal(rdbuf.begin(), rdbuf.end(), data.begin()));
185 for (size_t i = 0; i < blockSize * 2; i++)
186 {
187 EXPECT_EQ(reader.read(i, 1, rdbuf.data()), 1);
188 EXPECT_EQ(rdbuf[i], i);
189 }
190 EXPECT_EQ(reader.read(blockSize - 1, 2, rdbuf.data()), 2);
191 EXPECT_EQ(rdbuf[0], blockSize - 1);
192 EXPECT_EQ(rdbuf[1], blockSize);
193 }
194
TEST(FRUReaderTest,StartPastUnknownEOF)195 TEST(FRUReaderTest, StartPastUnknownEOF)
196 {
197 const std::vector<uint8_t> data = {};
198 auto getData = [&data](auto o, auto l, auto* b) {
199 return getDataTempl(data, o, l, b);
200 };
201 FRUReader reader(getData);
202
203 EXPECT_EQ(reader.read(1, 1, nullptr), 0);
204 }
205
TEST(FRUReaderTest,StartPastKnownEOF)206 TEST(FRUReaderTest, StartPastKnownEOF)
207 {
208 std::vector<uint8_t> data = {};
209 data.resize(blockSize / 2);
210 std::array<uint8_t, blockSize> blockData{};
211 auto getData = [&data](auto o, auto l, auto* b) {
212 return getDataTempl(data, o, l, b);
213 };
214 FRUReader reader(getData);
215
216 EXPECT_EQ(reader.read(0, blockSize, blockData.data()),
217 static_cast<ssize_t>(data.size()));
218 EXPECT_EQ(reader.read(data.size(), 1, nullptr), 0);
219 EXPECT_EQ(reader.read(data.size() + 1, 1, nullptr), 0);
220 EXPECT_EQ(reader.read(blockSize, 1, nullptr), 0);
221 EXPECT_EQ(reader.read(blockSize + 1, 1, nullptr), 0);
222 }
223
TEST(FRUReaderTest,DecreasingEOF)224 TEST(FRUReaderTest, DecreasingEOF)
225 {
226 const std::vector<uint8_t> data = {};
227 auto getData = [&data](auto o, auto l, auto* b) {
228 return getDataTempl(data, o, l, b);
229 };
230 FRUReader reader(getData);
231
232 EXPECT_EQ(reader.read(blockSize * 2, 1, nullptr), 0);
233 EXPECT_EQ(reader.read(blockSize + (blockSize / 2), 1, nullptr), 0);
234 EXPECT_EQ(reader.read(blockSize, 1, nullptr), 0);
235 EXPECT_EQ(reader.read(blockSize / 2, 1, nullptr), 0);
236 EXPECT_EQ(reader.read(0, 1, nullptr), 0);
237 }
238
TEST(FRUReaderTest,CacheHit)239 TEST(FRUReaderTest, CacheHit)
240 {
241 std::vector<uint8_t> data = {'X'};
242 std::array<uint8_t, blockSize> read1{};
243 std::array<uint8_t, blockSize> read2{};
244 auto getData = [&data](auto o, auto l, auto* b) {
245 return getDataTempl(data, o, l, b);
246 };
247 FRUReader reader(getData);
248
249 // cache hit should return the same data for the second read even if we
250 // change it behind the FRUReader's back after the first
251 EXPECT_EQ(reader.read(0, blockSize, read1.data()), 1);
252 data[0] = 'Y';
253 EXPECT_EQ(reader.read(0, blockSize, read2.data()), 1);
254 EXPECT_EQ(read1[0], read2[0]);
255 }
256
TEST(FRUReaderTest,ReadPastKnownEnd)257 TEST(FRUReaderTest, ReadPastKnownEnd)
258 {
259 const std::vector<uint8_t> data = {'X', 'Y'};
260 std::array<uint8_t, blockSize> rdbuf{};
261 auto getData = [&data](auto o, auto l, auto* b) {
262 return getDataTempl(data, o, l, b);
263 };
264 FRUReader reader(getData);
265
266 EXPECT_EQ(reader.read(0, data.size(), rdbuf.data()),
267 static_cast<ssize_t>(data.size()));
268 EXPECT_EQ(rdbuf[0], 'X');
269 EXPECT_EQ(rdbuf[1], 'Y');
270 EXPECT_EQ(reader.read(1, data.size(), rdbuf.data()),
271 static_cast<ssize_t>(data.size() - 1));
272 EXPECT_EQ(rdbuf[0], 'Y');
273 }
274
TEST(FRUReaderTest,MultiBlockRead)275 TEST(FRUReaderTest, MultiBlockRead)
276 {
277 std::vector<uint8_t> data = {};
278 data.resize(blockSize, 'X');
279 data.resize(2 * blockSize, 'Y');
280 std::array<uint8_t, 2 * blockSize> rdbuf{};
281 auto getData = [&data](auto o, auto l, auto* b) {
282 return getDataTempl(data, o, l, b);
283 };
284 FRUReader reader(getData);
285
286 EXPECT_EQ(reader.read(0, 2 * blockSize, rdbuf.data()),
287 static_cast<ssize_t>(2 * blockSize));
288 EXPECT_TRUE(std::equal(rdbuf.begin(), rdbuf.end(), data.begin()));
289 }
290
TEST(FRUReaderTest,ShrinkingEEPROM)291 TEST(FRUReaderTest, ShrinkingEEPROM)
292 {
293 std::vector<uint8_t> data = {};
294 data.resize(3 * blockSize, 'X');
295 std::array<uint8_t, blockSize> rdbuf{};
296 auto getData = [&data](auto o, auto l, auto* b) {
297 return getDataTempl(data, o, l, b);
298 };
299 FRUReader reader(getData);
300
301 EXPECT_EQ(reader.read(data.size() - 1, 2, rdbuf.data()), 1);
302 data.resize(blockSize);
303 EXPECT_EQ(reader.read(data.size() - 1, 2, rdbuf.data()), 1);
304 }
305
TEST(FindFRUHeaderTest,InvalidHeader)306 TEST(FindFRUHeaderTest, InvalidHeader)
307 {
308 const std::vector<uint8_t> data = {255, 16};
309 off_t offset = 0;
310 std::array<uint8_t, I2C_SMBUS_BLOCK_MAX> blockData{};
311 auto getData = [&data](auto o, auto l, auto* b) {
312 return getDataTempl(data, o, l, b);
313 };
314 FRUReader reader(getData);
315
316 EXPECT_FALSE(findFRUHeader(reader, "error", blockData, offset));
317 }
318
TEST(FindFRUHeaderTest,NoData)319 TEST(FindFRUHeaderTest, NoData)
320 {
321 const std::vector<uint8_t> data = {};
322 off_t offset = 0;
323 std::array<uint8_t, I2C_SMBUS_BLOCK_MAX> blockData{};
324 auto getData = [&data](auto o, auto l, auto* b) {
325 return getDataTempl(data, o, l, b);
326 };
327 FRUReader reader(getData);
328
329 EXPECT_FALSE(findFRUHeader(reader, "error", blockData, offset));
330 }
331
TEST(FindFRUHeaderTest,ValidHeader)332 TEST(FindFRUHeaderTest, ValidHeader)
333 {
334 const std::vector<uint8_t> data = {0x01, 0x00, 0x01, 0x02,
335 0x03, 0x04, 0x00, 0xf5};
336 off_t offset = 0;
337 std::array<uint8_t, I2C_SMBUS_BLOCK_MAX> blockData{};
338 auto getData = [&data](auto o, auto l, auto* b) {
339 return getDataTempl(data, o, l, b);
340 };
341 FRUReader reader(getData);
342
343 EXPECT_TRUE(findFRUHeader(reader, "error", blockData, offset));
344 EXPECT_EQ(0, offset);
345 }
346
TEST(FindFRUHeaderTest,TyanInvalidHeader)347 TEST(FindFRUHeaderTest, TyanInvalidHeader)
348 {
349 std::vector<uint8_t> data = {'$', 'T', 'Y', 'A', 'N', '$', 0, 0};
350 data.resize(0x6000 + I2C_SMBUS_BLOCK_MAX);
351 off_t offset = 0;
352 std::array<uint8_t, I2C_SMBUS_BLOCK_MAX> blockData{};
353 auto getData = [&data](auto o, auto l, auto* b) {
354 return getDataTempl(data, o, l, b);
355 };
356 FRUReader reader(getData);
357
358 EXPECT_FALSE(findFRUHeader(reader, "error", blockData, offset));
359 }
360
TEST(FindFRUHeaderTest,TyanNoData)361 TEST(FindFRUHeaderTest, TyanNoData)
362 {
363 const std::vector<uint8_t> data = {'$', 'T', 'Y', 'A', 'N', '$', 0, 0};
364 off_t offset = 0;
365 std::array<uint8_t, I2C_SMBUS_BLOCK_MAX> blockData{};
366 auto getData = [&data](auto o, auto l, auto* b) {
367 return getDataTempl(data, o, l, b);
368 };
369 FRUReader reader(getData);
370
371 EXPECT_FALSE(findFRUHeader(reader, "error", blockData, offset));
372 }
373
TEST(FindFRUHeaderTest,TyanValidHeader)374 TEST(FindFRUHeaderTest, TyanValidHeader)
375 {
376 std::vector<uint8_t> data = {'$', 'T', 'Y', 'A', 'N', '$', 0, 0};
377 data.resize(0x6000);
378 constexpr std::array<uint8_t, I2C_SMBUS_BLOCK_MAX> fruHeader = {
379 0x01, 0x00, 0x01, 0x02, 0x03, 0x04, 0x00, 0xf5};
380 copy(fruHeader.begin(), fruHeader.end(), back_inserter(data));
381
382 off_t offset = 0;
383 std::array<uint8_t, I2C_SMBUS_BLOCK_MAX> blockData{};
384 auto getData = [&data](auto o, auto l, auto* b) {
385 return getDataTempl(data, o, l, b);
386 };
387 FRUReader reader(getData);
388
389 EXPECT_TRUE(findFRUHeader(reader, "error", blockData, offset));
390 EXPECT_EQ(0x6000, offset);
391 }
392