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