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 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
TEST(FRUReaderTest,StartPastUnknownEOF)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
TEST(FRUReaderTest,StartPastKnownEOF)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
TEST(FRUReaderTest,DecreasingEOF)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
TEST(FRUReaderTest,CacheHit)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
TEST(FRUReaderTest,ReadPastKnownEnd)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
TEST(FRUReaderTest,MultiBlockRead)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
TEST(FRUReaderTest,ShrinkingEEPROM)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
TEST(FindFRUHeaderTest,InvalidHeader)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
TEST(FindFRUHeaderTest,NoData)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
TEST(FindFRUHeaderTest,ValidHeader)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
TEST(FindFRUHeaderTest,TyanInvalidHeader)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
TEST(FindFRUHeaderTest,TyanNoData)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
TEST(FindFRUHeaderTest,TyanValidHeader)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