1 /**
2  * Copyright © 2018 Intel Corporation
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 #define SD_JOURNAL_SUPPRESS_LOCATION
17 
18 #include <systemd/sd-journal.h>
19 
20 #include <ipmid/api.hpp>
21 #include <ipmid/message.hpp>
22 
23 #include <gtest/gtest.h>
24 
25 TEST(Payload, InputSize)
26 {
27     std::vector<uint8_t> i = {0xbf, 0x04, 0x86, 0x00, 0x02};
28     size_t input_size = i.size();
29     ipmi::message::Payload p(std::forward<std::vector<uint8_t>>(i));
30     ASSERT_EQ(input_size, p.size());
31 }
32 
33 TEST(Payload, OutputSize)
34 {
35     ipmi::message::Payload p;
36     ASSERT_EQ(0, p.size());
37     std::vector<uint8_t> i = {0xbf, 0x04, 0x86, 0x00, 0x02};
38     p.pack(i);
39     ASSERT_EQ(i.size(), p.size());
40     p.pack(i);
41     p.pack(i);
42     ASSERT_EQ(3 * i.size(), p.size());
43 }
44 
45 TEST(Payload, Resize)
46 {
47     std::vector<uint8_t> i = {0xbf, 0x04, 0x86, 0x00, 0x02};
48     ipmi::message::Payload p;
49     p.pack(i);
50     p.resize(16);
51     ASSERT_EQ(p.size(), 16);
52 }
53 
54 TEST(Payload, Data)
55 {
56     std::vector<uint8_t> i = {0xbf, 0x04, 0x86, 0x00, 0x02};
57     ipmi::message::Payload p;
58     p.pack(i);
59     ASSERT_NE(nullptr, p.data());
60 }
61 
62 TEST(PayloadResponse, Append)
63 {
64     std::string s("0123456789abcdef");
65     ipmi::message::Payload p;
66     p.append(s.data(), s.data() + s.size());
67     ASSERT_EQ(s.size(), p.size());
68 }
69 
70 TEST(PayloadResponse, AppendDrain)
71 {
72     std::string s("0123456789abcdef");
73     ipmi::message::Payload p;
74     bool b = true;
75     // first pack a lone bit
76     p.pack(b);
77     p.append(s.data(), s.data() + s.size());
78     // append will 'drain' first, padding the lone bit into a full byte
79     ASSERT_EQ(s.size() + 1, p.size());
80 }
81 
82 TEST(PayloadResponse, AppendBits)
83 {
84     ipmi::message::Payload p;
85     p.appendBits(3, 0b101);
86     ASSERT_EQ(p.bitStream, 0b101);
87     p.appendBits(4, 0b1100);
88     ASSERT_EQ(p.bitStream, 0b1100101);
89     p.appendBits(1, 0b1);
90     ASSERT_EQ(p.bitStream, 0);
91     ASSERT_EQ(p.bitCount, 0);
92     // appended 8 bits, should be one byte
93     ASSERT_EQ(p.size(), 1);
94     std::vector<uint8_t> k1 = {0b11100101};
95     ASSERT_EQ(p.raw, k1);
96     p.appendBits(7, 0b1110111);
97     // appended 7 more bits, should still be one byte
98     ASSERT_EQ(p.size(), 1);
99     p.drain();
100     // drain forces padding; should be two bytes now
101     ASSERT_EQ(p.size(), 2);
102     std::vector<uint8_t> k2 = {0b11100101, 0b01110111};
103     ASSERT_EQ(p.raw, k2);
104 }
105 
106 TEST(PayloadResponse, Drain16Bits)
107 {
108     ipmi::message::Payload p;
109     p.bitStream = 0b1011010011001111;
110     p.bitCount = 16;
111     p.drain();
112     ASSERT_EQ(p.size(), 2);
113     ASSERT_EQ(p.bitCount, 0);
114     ASSERT_EQ(p.bitStream, 0);
115     std::vector<uint8_t> k1 = {0b11001111, 0b10110100};
116     ASSERT_EQ(p.raw, k1);
117 }
118 
119 TEST(PayloadResponse, Drain15Bits)
120 {
121     ipmi::message::Payload p;
122     p.bitStream = 0b101101001100111;
123     p.bitCount = 15;
124     p.drain();
125     ASSERT_EQ(p.size(), 2);
126     ASSERT_EQ(p.bitCount, 0);
127     ASSERT_EQ(p.bitStream, 0);
128     std::vector<uint8_t> k1 = {0b1100111, 0b1011010};
129     ASSERT_EQ(p.raw, k1);
130 }
131 
132 TEST(PayloadResponse, Drain15BitsWholeBytesOnly)
133 {
134     ipmi::message::Payload p;
135     p.bitStream = 0b101101001100111;
136     p.bitCount = 15;
137     p.drain(true);
138     // only the first whole byte should have been 'drained' into p.raw
139     ASSERT_EQ(p.size(), 1);
140     ASSERT_EQ(p.bitCount, 7);
141     ASSERT_EQ(p.bitStream, 0b1011010);
142     std::vector<uint8_t> k1 = {0b1100111};
143     ASSERT_EQ(p.raw, k1);
144 }
145 
146 TEST(PayloadRequest, Pop)
147 {
148     std::vector<uint8_t> i = {0xbf, 0x04, 0x86, 0x00, 0x02};
149     ipmi::message::Payload p(std::forward<std::vector<uint8_t>>(i));
150     const auto& [vb, ve] = p.pop<uint8_t>(4);
151     std::vector<uint8_t> v(vb, ve);
152     std::vector<uint8_t> k = {0xbf, 0x04, 0x86, 0x00};
153     ASSERT_EQ(v, k);
154 }
155 
156 TEST(PayloadRequest, FillBits)
157 {
158     std::vector<uint8_t> i = {0xbf, 0x04, 0x86, 0x00, 0x02};
159     ipmi::message::Payload p(std::forward<std::vector<uint8_t>>(i));
160     p.fillBits(5);
161     ASSERT_FALSE(p.unpackError);
162     ASSERT_EQ(p.bitStream, 0xbf);
163     ASSERT_EQ(p.bitCount, 8);
164     // should still have 5 bits available, no change
165     p.fillBits(5);
166     ASSERT_FALSE(p.unpackError);
167     ASSERT_EQ(p.bitStream, 0xbf);
168     ASSERT_EQ(p.bitCount, 8);
169     // discard 5 bits (low order)
170     p.popBits(5);
171     // should add another byte into the stream (high order)
172     p.fillBits(5);
173     ASSERT_FALSE(p.unpackError);
174     ASSERT_EQ(p.bitStream, 0x25);
175     ASSERT_EQ(p.bitCount, 11);
176 }
177 
178 TEST(PayloadRequest, FillBitsTooManyBits)
179 {
180     std::vector<uint8_t> i = {1, 2, 3, 4, 5, 6, 7, 8, 9};
181     ipmi::message::Payload p(std::forward<std::vector<uint8_t>>(i));
182     p.fillBits(72);
183     ASSERT_TRUE(p.unpackError);
184 }
185 
186 TEST(PayloadRequest, FillBitsNotEnoughBytes)
187 {
188     std::vector<uint8_t> i = {1, 2, 3, 4};
189     ipmi::message::Payload p(std::forward<std::vector<uint8_t>>(i));
190     p.fillBits(48);
191     ASSERT_TRUE(p.unpackError);
192 }
193 
194 TEST(PayloadRequest, PopBits)
195 {
196     std::vector<uint8_t> i = {0xbf, 0x04, 0x86, 0x00, 0x02};
197     ipmi::message::Payload p(std::forward<std::vector<uint8_t>>(i));
198     p.fillBits(4);
199     uint8_t v = p.popBits(4);
200     ASSERT_FALSE(p.unpackError);
201     ASSERT_EQ(p.bitStream, 0x0b);
202     ASSERT_EQ(p.bitCount, 4);
203     ASSERT_EQ(v, 0x0f);
204 }
205 
206 TEST(PayloadRequest, PopBitsNoFillBits)
207 {
208     std::vector<uint8_t> i = {0xbf, 0x04, 0x86, 0x00, 0x02};
209     ipmi::message::Payload p(std::forward<std::vector<uint8_t>>(i));
210     p.popBits(4);
211     ASSERT_TRUE(p.unpackError);
212 }
213 
214 TEST(PayloadRequest, DiscardBits)
215 {
216     std::vector<uint8_t> i = {0xbf, 0x04, 0x86, 0x00, 0x02};
217     ipmi::message::Payload p(std::forward<std::vector<uint8_t>>(i));
218     p.fillBits(5);
219     ASSERT_FALSE(p.unpackError);
220     ASSERT_EQ(p.bitStream, 0xbf);
221     ASSERT_EQ(p.bitCount, 8);
222     p.discardBits();
223     ASSERT_FALSE(p.unpackError);
224     ASSERT_EQ(p.bitStream, 0);
225     ASSERT_EQ(p.bitCount, 0);
226 }
227 
228 TEST(PayloadRequest, FullyUnpacked)
229 {
230     std::vector<uint8_t> i = {0xbf, 0x04, 0x86, 0x00, 0x02};
231     ipmi::message::Payload p(std::forward<std::vector<uint8_t>>(i));
232     uint32_t v1;
233     p.unpack(v1);
234     // still one remaining byte
235     ASSERT_FALSE(p.fullyUnpacked());
236     p.fillBits(3);
237     p.popBits(3);
238     // still five remaining bits
239     ASSERT_FALSE(p.fullyUnpacked());
240     p.fillBits(5);
241     p.popBits(5);
242     // fully unpacked, should be no errors
243     ASSERT_TRUE(p.fullyUnpacked());
244     p.fillBits(4);
245     // fullyUnpacked fails because an attempt to unpack too many bytes
246     ASSERT_FALSE(p.fullyUnpacked());
247 }
248 
249 TEST(PayloadRequest, ResetInternal)
250 {
251     std::vector<uint8_t> i = {0xbf, 0x04, 0x86, 0x00, 0x02};
252     ipmi::message::Payload p(std::forward<std::vector<uint8_t>>(i));
253     p.fillBits(4);
254     p.unpackError = true;
255     p.reset();
256     ASSERT_EQ(p.rawIndex, 0);
257     ASSERT_EQ(p.bitStream, 0);
258     ASSERT_EQ(p.bitCount, 0);
259     ASSERT_FALSE(p.unpackError);
260 }
261 
262 TEST(PayloadRequest, ResetUsage)
263 {
264     // Payload.reset is used to rewind the unpacking to the initial
265     // state. This is needed so that OEM commands can unpack the group
266     // number or the IANA to determine which handler needs to be called
267     std::vector<uint8_t> i = {0x04, 0x86};
268     ipmi::message::Payload p(std::forward<std::vector<uint8_t>>(i));
269     uint8_t v1;
270     // check that the number of bytes matches
271     ASSERT_EQ(p.unpack(v1), 0);
272     // check that the payload was not fully unpacked
273     ASSERT_FALSE(p.fullyUnpacked());
274     uint8_t k1 = 0x04;
275     // check that the bytes were correctly unpacked (LSB first)
276     ASSERT_EQ(v1, k1);
277     // do a reset on the payload
278     p.reset();
279     // unpack a uint16
280     uint16_t v2;
281     // check that the number of bytes matches
282     ASSERT_EQ(p.unpack(v2), 0);
283     // check that the payload was fully unpacked
284     ASSERT_TRUE(p.fullyUnpacked());
285     uint16_t k2 = 0x8604;
286     // check that the bytes were correctly unpacked (LSB first)
287     ASSERT_EQ(v2, k2);
288 }
289 
290 TEST(PayloadRequest, PartialPayload)
291 {
292     std::vector<uint8_t> i = {0xbf, 0x04, 0x86, 0x00, 0x02};
293     ipmi::message::Payload p(std::forward<std::vector<uint8_t>>(i));
294     uint8_t v1;
295     ipmi::message::Payload localPayload;
296     // check that the number of bytes matches
297     ASSERT_EQ(p.unpack(v1, localPayload), 0);
298     // check that the payload was partially unpacked and not in error
299     ASSERT_FALSE(p.fullyUnpacked());
300     ASSERT_FALSE(p.unpackError);
301     // check that the 'extracted' payload is not fully unpacked
302     ASSERT_FALSE(localPayload.fullyUnpacked());
303     uint8_t k1 = 0xbf;
304     // check that the bytes were correctly unpacked (LSB first)
305     ASSERT_EQ(v1, k1);
306     uint32_t v2;
307     // unpack using the 'extracted' payload
308     ASSERT_EQ(localPayload.unpack(v2), 0);
309     ASSERT_TRUE(localPayload.fullyUnpacked());
310     uint32_t k2 = 0x02008604;
311     ASSERT_EQ(v2, k2);
312 }
313 
314 std::vector<std::string> logs;
315 
316 extern "C" {
317 int sd_journal_send(const char* format, ...)
318 {
319     logs.push_back(format);
320     return 0;
321 }
322 
323 int sd_journal_send_with_location(const char* file, const char* line,
324                                   const char* func, const char* format, ...)
325 {
326     logs.push_back(format);
327     return 0;
328 }
329 }
330 
331 class PayloadLogging : public testing::Test
332 {
333   public:
334     void SetUp()
335     {
336         logs.clear();
337     }
338 };
339 
340 TEST_F(PayloadLogging, TrailingOk)
341 {
342     {
343         ipmi::message::Payload p({1, 2});
344     }
345     EXPECT_EQ(logs.size(), 0);
346 }
347 
348 TEST_F(PayloadLogging, EnforcingUnchecked)
349 {
350     {
351         ipmi::message::Payload p({1, 2});
352         p.trailingOk = false;
353     }
354     EXPECT_EQ(logs.size(), 1);
355 }
356 
357 TEST_F(PayloadLogging, EnforcingUncheckedUnpacked)
358 {
359     {
360         ipmi::message::Payload p({1, 2});
361         p.trailingOk = false;
362         uint8_t out;
363         p.unpack(out, out);
364     }
365     EXPECT_EQ(logs.size(), 1);
366 }
367 
368 TEST_F(PayloadLogging, EnforcingUncheckedError)
369 {
370     {
371         ipmi::message::Payload p({1, 2});
372         p.trailingOk = false;
373         uint32_t out;
374         p.unpack(out);
375     }
376     EXPECT_EQ(logs.size(), 0);
377 }
378 
379 TEST_F(PayloadLogging, EnforcingChecked)
380 {
381     {
382         ipmi::message::Payload p({1, 2});
383         p.trailingOk = false;
384         EXPECT_FALSE(p.fullyUnpacked());
385     }
386     EXPECT_EQ(logs.size(), 0);
387 }
388 
389 TEST_F(PayloadLogging, EnforcingCheckedUnpacked)
390 {
391     {
392         ipmi::message::Payload p({1, 2});
393         p.trailingOk = false;
394         uint8_t out;
395         p.unpack(out, out);
396         EXPECT_TRUE(p.fullyUnpacked());
397     }
398     EXPECT_EQ(logs.size(), 0);
399 }
400 
401 TEST_F(PayloadLogging, EnforcingUnpackPayload)
402 {
403     {
404         ipmi::message::Payload p;
405         {
406             ipmi::message::Payload q({1, 2});
407             q.trailingOk = false;
408             q.unpack(p);
409         }
410         EXPECT_EQ(logs.size(), 0);
411     }
412     EXPECT_EQ(logs.size(), 1);
413 }
414 
415 TEST_F(PayloadLogging, EnforcingMove)
416 {
417     {
418         ipmi::message::Payload p;
419         {
420             ipmi::message::Payload q({1, 2});
421             q.trailingOk = false;
422             p = std::move(q);
423         }
424         EXPECT_EQ(logs.size(), 0);
425     }
426     EXPECT_EQ(logs.size(), 1);
427 }
428