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