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