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 #include <ipmid/api.hpp>
17 #include <ipmid/message.hpp>
18 
19 #include <gtest/gtest.h>
20 
21 TEST(Payload, InputSize)
22 {
23     std::vector<uint8_t> i = {0xbf, 0x04, 0x86, 0x00, 0x02};
24     size_t input_size = i.size();
25     ipmi::message::Payload p(std::forward<std::vector<uint8_t>>(i));
26     ASSERT_EQ(input_size, p.size());
27 }
28 
29 TEST(Payload, OutputSize)
30 {
31     ipmi::message::Payload p;
32     ASSERT_EQ(0, p.size());
33     std::vector<uint8_t> i = {0xbf, 0x04, 0x86, 0x00, 0x02};
34     p.pack(i);
35     ASSERT_EQ(i.size(), p.size());
36     p.pack(i);
37     p.pack(i);
38     ASSERT_EQ(3 * i.size(), p.size());
39 }
40 
41 TEST(Payload, Resize)
42 {
43     std::vector<uint8_t> i = {0xbf, 0x04, 0x86, 0x00, 0x02};
44     ipmi::message::Payload p;
45     p.pack(i);
46     p.resize(16);
47     ASSERT_EQ(p.size(), 16);
48 }
49 
50 TEST(Payload, Data)
51 {
52     std::vector<uint8_t> i = {0xbf, 0x04, 0x86, 0x00, 0x02};
53     ipmi::message::Payload p;
54     p.pack(i);
55     ASSERT_NE(nullptr, p.data());
56 }
57 
58 TEST(PayloadResponse, Append)
59 {
60     std::string s("0123456789abcdef");
61     ipmi::message::Payload p;
62     p.append(s.data(), s.data() + s.size());
63     ASSERT_EQ(s.size(), p.size());
64 }
65 
66 TEST(PayloadResponse, AppendDrain)
67 {
68     std::string s("0123456789abcdef");
69     ipmi::message::Payload p;
70     bool b = true;
71     // first pack a lone bit
72     p.pack(b);
73     p.append(s.data(), s.data() + s.size());
74     // append will 'drain' first, padding the lone bit into a full byte
75     ASSERT_EQ(s.size() + 1, p.size());
76 }
77 
78 TEST(PayloadResponse, AppendBits)
79 {
80     ipmi::message::Payload p;
81     p.appendBits(3, 0b101);
82     ASSERT_EQ(p.bitStream, 0b101);
83     p.appendBits(4, 0b1100);
84     ASSERT_EQ(p.bitStream, 0b1100101);
85     p.appendBits(1, 0b1);
86     ASSERT_EQ(p.bitStream, 0);
87     ASSERT_EQ(p.bitCount, 0);
88     // appended 8 bits, should be one byte
89     ASSERT_EQ(p.size(), 1);
90     std::vector<uint8_t> k1 = {0b11100101};
91     ASSERT_EQ(p.raw, k1);
92     p.appendBits(7, 0b1110111);
93     // appended 7 more bits, should still be one byte
94     ASSERT_EQ(p.size(), 1);
95     p.drain();
96     // drain forces padding; should be two bytes now
97     ASSERT_EQ(p.size(), 2);
98     std::vector<uint8_t> k2 = {0b11100101, 0b01110111};
99     ASSERT_EQ(p.raw, k2);
100 }
101 
102 TEST(PayloadResponse, Drain16Bits)
103 {
104     ipmi::message::Payload p;
105     p.bitStream = 0b1011010011001111;
106     p.bitCount = 16;
107     p.drain();
108     ASSERT_EQ(p.size(), 2);
109     ASSERT_EQ(p.bitCount, 0);
110     ASSERT_EQ(p.bitStream, 0);
111     std::vector<uint8_t> k1 = {0b11001111, 0b10110100};
112     ASSERT_EQ(p.raw, k1);
113 }
114 
115 TEST(PayloadResponse, Drain15Bits)
116 {
117     ipmi::message::Payload p;
118     p.bitStream = 0b101101001100111;
119     p.bitCount = 15;
120     p.drain();
121     ASSERT_EQ(p.size(), 2);
122     ASSERT_EQ(p.bitCount, 0);
123     ASSERT_EQ(p.bitStream, 0);
124     std::vector<uint8_t> k1 = {0b1100111, 0b1011010};
125     ASSERT_EQ(p.raw, k1);
126 }
127 
128 TEST(PayloadResponse, Drain15BitsWholeBytesOnly)
129 {
130     ipmi::message::Payload p;
131     p.bitStream = 0b101101001100111;
132     p.bitCount = 15;
133     p.drain(true);
134     // only the first whole byte should have been 'drained' into p.raw
135     ASSERT_EQ(p.size(), 1);
136     ASSERT_EQ(p.bitCount, 7);
137     ASSERT_EQ(p.bitStream, 0b1011010);
138     std::vector<uint8_t> k1 = {0b1100111};
139     ASSERT_EQ(p.raw, k1);
140 }
141 
142 TEST(PayloadRequest, Pop)
143 {
144     std::vector<uint8_t> i = {0xbf, 0x04, 0x86, 0x00, 0x02};
145     ipmi::message::Payload p(std::forward<std::vector<uint8_t>>(i));
146     const auto& [vb, ve] = p.pop<uint8_t>(4);
147     std::vector<uint8_t> v(vb, ve);
148     std::vector<uint8_t> k = {0xbf, 0x04, 0x86, 0x00};
149     ASSERT_EQ(v, k);
150 }
151 
152 TEST(PayloadRequest, FillBits)
153 {
154     std::vector<uint8_t> i = {0xbf, 0x04, 0x86, 0x00, 0x02};
155     ipmi::message::Payload p(std::forward<std::vector<uint8_t>>(i));
156     p.fillBits(5);
157     ASSERT_FALSE(p.unpackError);
158     ASSERT_EQ(p.bitStream, 0xbf);
159     ASSERT_EQ(p.bitCount, 8);
160     // should still have 5 bits available, no change
161     p.fillBits(5);
162     ASSERT_FALSE(p.unpackError);
163     ASSERT_EQ(p.bitStream, 0xbf);
164     ASSERT_EQ(p.bitCount, 8);
165     // discard 5 bits (low order)
166     p.popBits(5);
167     // should add another byte into the stream (high order)
168     p.fillBits(5);
169     ASSERT_FALSE(p.unpackError);
170     ASSERT_EQ(p.bitStream, 0x25);
171     ASSERT_EQ(p.bitCount, 11);
172 }
173 
174 TEST(PayloadRequest, FillBitsTooManyBits)
175 {
176     std::vector<uint8_t> i = {1, 2, 3, 4, 5, 6, 7, 8, 9};
177     ipmi::message::Payload p(std::forward<std::vector<uint8_t>>(i));
178     p.fillBits(72);
179     ASSERT_TRUE(p.unpackError);
180 }
181 
182 TEST(PayloadRequest, FillBitsNotEnoughBytes)
183 {
184     std::vector<uint8_t> i = {1, 2, 3, 4};
185     ipmi::message::Payload p(std::forward<std::vector<uint8_t>>(i));
186     p.fillBits(48);
187     ASSERT_TRUE(p.unpackError);
188 }
189 
190 TEST(PayloadRequest, PopBits)
191 {
192     std::vector<uint8_t> i = {0xbf, 0x04, 0x86, 0x00, 0x02};
193     ipmi::message::Payload p(std::forward<std::vector<uint8_t>>(i));
194     p.fillBits(4);
195     uint8_t v = p.popBits(4);
196     ASSERT_FALSE(p.unpackError);
197     ASSERT_EQ(p.bitStream, 0x0b);
198     ASSERT_EQ(p.bitCount, 4);
199     ASSERT_EQ(v, 0x0f);
200 }
201 
202 TEST(PayloadRequest, PopBitsNoFillBits)
203 {
204     std::vector<uint8_t> i = {0xbf, 0x04, 0x86, 0x00, 0x02};
205     ipmi::message::Payload p(std::forward<std::vector<uint8_t>>(i));
206     p.popBits(4);
207     ASSERT_TRUE(p.unpackError);
208 }
209 
210 TEST(PayloadRequest, DiscardBits)
211 {
212     std::vector<uint8_t> i = {0xbf, 0x04, 0x86, 0x00, 0x02};
213     ipmi::message::Payload p(std::forward<std::vector<uint8_t>>(i));
214     p.fillBits(5);
215     ASSERT_FALSE(p.unpackError);
216     ASSERT_EQ(p.bitStream, 0xbf);
217     ASSERT_EQ(p.bitCount, 8);
218     p.discardBits();
219     ASSERT_FALSE(p.unpackError);
220     ASSERT_EQ(p.bitStream, 0);
221     ASSERT_EQ(p.bitCount, 0);
222 }
223 
224 TEST(PayloadRequest, FullyUnpacked)
225 {
226     std::vector<uint8_t> i = {0xbf, 0x04, 0x86, 0x00, 0x02};
227     ipmi::message::Payload p(std::forward<std::vector<uint8_t>>(i));
228     uint32_t v1;
229     p.unpack(v1);
230     // still one remaining byte
231     ASSERT_FALSE(p.fullyUnpacked());
232     p.fillBits(3);
233     p.popBits(3);
234     // still five remaining bits
235     ASSERT_FALSE(p.fullyUnpacked());
236     p.fillBits(5);
237     p.popBits(5);
238     // fully unpacked, should be no errors
239     ASSERT_TRUE(p.fullyUnpacked());
240     p.fillBits(4);
241     // fullyUnpacked fails because an attempt to unpack too many bytes
242     ASSERT_FALSE(p.fullyUnpacked());
243 }
244 
245 TEST(PayloadRequest, ResetInternal)
246 {
247     std::vector<uint8_t> i = {0xbf, 0x04, 0x86, 0x00, 0x02};
248     ipmi::message::Payload p(std::forward<std::vector<uint8_t>>(i));
249     p.fillBits(4);
250     p.unpackError = true;
251     p.reset();
252     ASSERT_EQ(p.rawIndex, 0);
253     ASSERT_EQ(p.bitStream, 0);
254     ASSERT_EQ(p.bitCount, 0);
255     ASSERT_FALSE(p.unpackError);
256 }
257 
258 TEST(PayloadRequest, ResetUsage)
259 {
260     // Payload.reset is used to rewind the unpacking to the initial
261     // state. This is needed so that OEM commands can unpack the group
262     // number or the IANA to determine which handler needs to be called
263     std::vector<uint8_t> i = {0x04, 0x86};
264     ipmi::message::Payload p(std::forward<std::vector<uint8_t>>(i));
265     uint8_t v1;
266     // check that the number of bytes matches
267     ASSERT_EQ(p.unpack(v1), 0);
268     // check that the payload was not fully unpacked
269     ASSERT_FALSE(p.fullyUnpacked());
270     uint8_t k1 = 0x04;
271     // check that the bytes were correctly unpacked (LSB first)
272     ASSERT_EQ(v1, k1);
273     // do a reset on the payload
274     p.reset();
275     // unpack a uint16
276     uint16_t v2;
277     // check that the number of bytes matches
278     ASSERT_EQ(p.unpack(v2), 0);
279     // check that the payload was fully unpacked
280     ASSERT_TRUE(p.fullyUnpacked());
281     uint16_t k2 = 0x8604;
282     // check that the bytes were correctly unpacked (LSB first)
283     ASSERT_EQ(v2, k2);
284 }
285 
286 TEST(PayloadRequest, PartialPayload)
287 {
288     std::vector<uint8_t> i = {0xbf, 0x04, 0x86, 0x00, 0x02};
289     ipmi::message::Payload p(std::forward<std::vector<uint8_t>>(i));
290     uint8_t v1;
291     ipmi::message::Payload localPayload;
292     // check that the number of bytes matches
293     ASSERT_EQ(p.unpack(v1, localPayload), 0);
294     // check that the payload was partially unpacked and not in error
295     ASSERT_FALSE(p.fullyUnpacked());
296     ASSERT_FALSE(p.unpackError);
297     // check that the 'extracted' payload is not fully unpacked
298     ASSERT_FALSE(localPayload.fullyUnpacked());
299     uint8_t k1 = 0xbf;
300     // check that the bytes were correctly unpacked (LSB first)
301     ASSERT_EQ(v1, k1);
302     uint32_t v2;
303     // unpack using the 'extracted' payload
304     ASSERT_EQ(localPayload.unpack(v2), 0);
305     ASSERT_TRUE(localPayload.fullyUnpacked());
306     uint32_t k2 = 0x02008604;
307     ASSERT_EQ(v2, k2);
308 }
309