1 // Copyright 2022 Google LLC 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 #include "errors.hpp" 16 #include "handler.hpp" 17 #include "handler_impl.hpp" 18 19 #include <systemd/sd-bus.h> 20 21 #include <nlohmann/json.hpp> 22 #include <sdbusplus/message.hpp> 23 #include <sdbusplus/test/sdbus_mock.hpp> 24 #include <stdplus/print.hpp> 25 26 #include <charconv> 27 #include <filesystem> 28 #include <fstream> 29 #include <functional> 30 #include <optional> 31 #include <string> 32 #include <tuple> 33 34 #include <gtest/gtest.h> 35 36 namespace google 37 { 38 namespace ipmi 39 { 40 41 using testing::_; 42 using testing::Return; 43 44 TEST(HandlerTest, EthCheckValidHappy) 45 { 46 Handler h; 47 std::tuple<std::uint8_t, std::string> result = h.getEthDetails("et"); 48 EXPECT_EQ(12, std::get<0>(result)); 49 EXPECT_STREQ("et", std::get<1>(result).c_str()); 50 } 51 52 TEST(HandlerTest, CableCheckIllegalPath) 53 { 54 Handler h; 55 EXPECT_THROW(h.getRxPackets("eth0/../../"), IpmiException); 56 } 57 58 TEST(HandlerTest, readNameFromConfigInstanceVariety) 59 { 60 // Make sure it handles the failures and successes as we expect. 61 struct testCase 62 { 63 std::string type; 64 std::uint8_t instance; 65 std::string expectedName; 66 }; 67 68 std::vector<testCase> tests = { 69 {"cpu", 5, ""}, 70 {"cpu", 3, "CPU2"}, 71 }; 72 73 auto j2 = R"( 74 { 75 "cpu": [ 76 {"instance": 1, "name": "CPU0"}, 77 {"instance": 2, "name": "CPU1"}, 78 {"instance": 3, "name": "CPU2"}, 79 {"instance": 4, "name": "CPU3"} 80 ] 81 } 82 )"_json; 83 84 for (const auto& test : tests) 85 { 86 EXPECT_STREQ(test.expectedName.c_str(), 87 readNameFromConfig(test.type, test.instance, j2).c_str()); 88 } 89 } 90 91 // TODO: If we can test with phosphor-logging in the future, there are more 92 // failure cases. 93 94 TEST(HandlerTest, getEntityNameWithNameNotFoundExcepts) 95 { 96 const char* testFilename = "test.json"; 97 std::string contents = R"({"cpu": [{"instance": 1, "name": "CPU0"}]})"; 98 std::ofstream outputJson(testFilename); 99 outputJson << contents; 100 outputJson.flush(); 101 outputJson.close(); 102 103 Handler h(testFilename); 104 EXPECT_THROW(h.getEntityName(0x03, 2), IpmiException); 105 (void)std::remove(testFilename); 106 } 107 108 TEST(HandlerTest, getEntityNameWithNameFoundReturnsIt) 109 { 110 const char* testFilename = "test.json"; 111 std::string contents = R"({"cpu": [{"instance": 1, "name": "CPU0"}]})"; 112 std::ofstream outputJson(testFilename); 113 outputJson << contents; 114 outputJson.flush(); 115 outputJson.close(); 116 117 Handler h(testFilename); 118 EXPECT_STREQ("CPU0", h.getEntityName(0x03, 1).c_str()); 119 (void)std::remove(testFilename); 120 } 121 122 using ::testing::_; 123 using ::testing::AnyNumber; 124 using ::testing::ContainerEq; 125 using ::testing::DoAll; 126 using ::testing::ElementsAre; 127 using ::testing::Eq; 128 using ::testing::IsNull; 129 using ::testing::MatcherCast; 130 using ::testing::NotNull; 131 using ::testing::Pointee; 132 using ::testing::Return; 133 using ::testing::ReturnNull; 134 using ::testing::SafeMatcherCast; 135 using ::testing::SetArgPointee; 136 using ::testing::StrEq; 137 using ::testing::StrictMock; 138 using ::testing::StrNe; 139 using ::testing::WithArg; 140 141 class MockDbusHandler : public Handler 142 { 143 public: 144 MockDbusHandler(sdbusplus::SdBusMock& mock, 145 const std::string& config = "") : 146 Handler(config), mock_(&mock) 147 {} 148 149 protected: 150 sdbusplus::bus_t getDbus() const override 151 { 152 return sdbusplus::get_mocked_new( 153 const_cast<sdbusplus::SdBusMock*>(mock_)); 154 } 155 156 private: 157 sdbusplus::SdBusMock* mock_; 158 }; 159 160 ACTION_TEMPLATE(AssignReadVal, HAS_1_TEMPLATE_PARAMS(typename, T), 161 AND_1_VALUE_PARAMS(val)) 162 { 163 *static_cast<T*>(arg2) = val; 164 } 165 166 ACTION_P(TraceDbus, msg) 167 { 168 stdplus::print(stderr, "{}\n", msg); 169 } 170 171 ACTION_P(TraceDbus2, msg) 172 { 173 stdplus::print(stderr, "{}({:02x})\n", msg, 174 *static_cast<const uint8_t*>(arg2)); 175 } 176 177 constexpr char object_path[] = "/com/google/customAccel/test/path"; 178 constexpr char property_grpc[] = "com.google.custom_accel.gRPC"; 179 constexpr char value_port[] = "Port"; 180 constexpr uint32_t port = 5000; 181 182 constexpr char SD_BUS_TYPE_BYTE_STR[] = {SD_BUS_TYPE_BYTE, '\0'}; 183 184 // Returns an object that looks like: 185 // "/com/google/customAccel/test/path": { 186 // "com.google.custom_accel.gRPC" : { 187 // "Port" : { 188 // "type" : "u", 189 // "data" : 5000 190 // } 191 // } 192 // } 193 void ExpectGetManagedObjects(StrictMock<sdbusplus::SdBusMock>& mock, 194 const char* obj_path = object_path) 195 { 196 ::testing::InSequence s; 197 198 // These must be nullptr or sd_bus_message_unref will seg fault. 199 constexpr sd_bus_message* method = nullptr; 200 constexpr sd_bus_message* msg = nullptr; 201 202 EXPECT_CALL(mock, sd_bus_message_new_method_call( 203 _, // sd_bus *bus, 204 NotNull(), // sd_bus_message **m 205 StrEq("com.google.custom_accel"), StrEq("/"), 206 StrEq("org.freedesktop.DBus.ObjectManager"), 207 StrEq("GetManagedObjects"))) 208 .WillOnce(DoAll(SetArgPointee<1>(method), Return(0))); 209 210 EXPECT_CALL(mock, sd_bus_call(_, // sd_bus *bus, 211 method, // sd_bus_message *m 212 _, // uint64_t timeout 213 NotNull(), // sd_bus_error *ret_error 214 NotNull())) // sd_bus_message **reply 215 .WillOnce(DoAll(SetArgPointee<3>(SD_BUS_ERROR_NULL), 216 SetArgPointee<4>(msg), // reply 217 Return(0))); 218 219 EXPECT_CALL(mock, sd_bus_message_enter_container(msg, SD_BUS_TYPE_ARRAY, 220 StrEq("{oa{sa{sv}}}"))) 221 .WillOnce(Return(1)); 222 223 EXPECT_CALL(mock, sd_bus_message_at_end(msg, 0)).WillOnce(Return(0)); 224 225 EXPECT_CALL(mock, sd_bus_message_enter_container( 226 msg, SD_BUS_TYPE_DICT_ENTRY, StrEq("oa{sa{sv}}"))) 227 .WillOnce(Return(1)); 228 229 EXPECT_CALL(mock, sd_bus_message_read_basic(msg, SD_BUS_TYPE_OBJECT_PATH, 230 NotNull())) 231 .WillOnce(DoAll(AssignReadVal<const char*>(obj_path), Return(1))); 232 233 EXPECT_CALL(mock, sd_bus_message_enter_container(msg, SD_BUS_TYPE_ARRAY, 234 StrEq("{sa{sv}}"))) 235 .WillOnce(Return(1)); 236 237 EXPECT_CALL(mock, sd_bus_message_at_end(msg, 0)).WillOnce(Return(0)); 238 239 EXPECT_CALL(mock, sd_bus_message_enter_container( 240 msg, SD_BUS_TYPE_DICT_ENTRY, StrEq("sa{sv}"))) 241 .WillOnce(Return(1)); 242 243 EXPECT_CALL(mock, 244 sd_bus_message_read_basic(msg, SD_BUS_TYPE_STRING, NotNull())) 245 .WillOnce(DoAll(AssignReadVal<const char*>(property_grpc), Return(1))); 246 247 EXPECT_CALL(mock, sd_bus_message_enter_container(msg, SD_BUS_TYPE_ARRAY, 248 StrEq("{sv}"))) 249 .WillOnce(Return(1)); 250 251 EXPECT_CALL(mock, sd_bus_message_at_end(msg, 0)).WillOnce(Return(0)); 252 253 EXPECT_CALL(mock, sd_bus_message_enter_container( 254 msg, SD_BUS_TYPE_DICT_ENTRY, StrEq("sv"))) 255 .WillOnce(Return(1)); 256 257 EXPECT_CALL(mock, 258 sd_bus_message_read_basic(msg, SD_BUS_TYPE_STRING, NotNull())) 259 .WillOnce(DoAll(AssignReadVal<const char*>(value_port), Return(1))); 260 261 EXPECT_CALL( 262 mock, sd_bus_message_verify_type(msg, SD_BUS_TYPE_VARIANT, StrNe("u"))) 263 .Times(AnyNumber()) 264 .WillRepeatedly(Return(0)); 265 266 EXPECT_CALL( 267 mock, sd_bus_message_verify_type(msg, SD_BUS_TYPE_VARIANT, StrEq("u"))) 268 .WillOnce(Return(1)); 269 270 EXPECT_CALL(mock, sd_bus_message_enter_container(msg, SD_BUS_TYPE_VARIANT, 271 StrEq("u"))) 272 .WillOnce(Return(1)); 273 274 EXPECT_CALL(mock, 275 sd_bus_message_read_basic(msg, SD_BUS_TYPE_UINT32, NotNull())) 276 .WillOnce(DoAll(AssignReadVal<uint32_t>(port), Return(0))); 277 278 EXPECT_CALL( 279 mock, sd_bus_message_verify_type(msg, SD_BUS_TYPE_VARIANT, StrNe("u"))) 280 .Times(AnyNumber()) 281 .WillRepeatedly(Return(0)); 282 283 EXPECT_CALL(mock, sd_bus_message_exit_container(msg)) 284 .WillOnce(Return(1)) 285 .WillOnce(Return(1)); 286 287 EXPECT_CALL(mock, sd_bus_message_at_end(msg, 0)).WillOnce(Return(1)); 288 289 EXPECT_CALL(mock, sd_bus_message_exit_container(msg)) 290 .WillOnce(Return(1)) 291 .WillOnce(Return(1)); 292 293 EXPECT_CALL(mock, sd_bus_message_at_end(msg, 0)).WillOnce(Return(1)); 294 295 EXPECT_CALL(mock, sd_bus_message_exit_container(msg)) 296 .WillOnce(Return(1)) 297 .WillOnce(Return(1)); 298 299 EXPECT_CALL(mock, sd_bus_message_at_end(msg, 0)).WillOnce(Return(1)); 300 301 EXPECT_CALL(mock, sd_bus_message_exit_container(msg)).WillOnce(Return(1)); 302 } 303 304 void ExpectSdBusError(StrictMock<sdbusplus::SdBusMock>& mock, 305 const std::string& service, const std::string& objPath, 306 const std::string& interface, const std::string& function) 307 { 308 EXPECT_CALL(mock, sd_bus_message_new_method_call( 309 _, // sd_bus *bus, 310 NotNull(), // sd_bus_message **m 311 StrEq(service), StrEq(objPath), StrEq(interface), 312 StrEq(function))) 313 .WillOnce(Return(-ENOTCONN)); 314 } 315 316 TEST(HandlerTest, accelOobDeviceCount_Success) 317 { 318 StrictMock<sdbusplus::SdBusMock> mock; 319 MockDbusHandler h(mock); 320 ExpectGetManagedObjects(mock); 321 EXPECT_EQ(1, h.accelOobDeviceCount()); 322 } 323 324 TEST(HandlerTest, accelOobDeviceCount_Fail) 325 { 326 StrictMock<sdbusplus::SdBusMock> mock; 327 MockDbusHandler h(mock); 328 ExpectSdBusError(mock, "com.google.custom_accel", "/", 329 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects"); 330 EXPECT_THROW(h.accelOobDeviceCount(), IpmiException); 331 } 332 333 TEST(HandlerTest, accelOobDeviceName_Success) 334 { 335 StrictMock<sdbusplus::SdBusMock> mock; 336 MockDbusHandler h(mock); 337 ExpectGetManagedObjects(mock); 338 EXPECT_EQ(std::string("test/path"), h.accelOobDeviceName(0)); 339 } 340 341 TEST(HandlerTest, accelOobDeviceName_Fail) 342 { 343 StrictMock<sdbusplus::SdBusMock> mock; 344 MockDbusHandler h(mock); 345 ExpectSdBusError(mock, "com.google.custom_accel", "/", 346 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects"); 347 EXPECT_THROW(h.accelOobDeviceName(0), IpmiException); 348 } 349 350 TEST(HandlerTest, accelOobDeviceName_OutOfRange) 351 { 352 StrictMock<sdbusplus::SdBusMock> mock; 353 MockDbusHandler h(mock); 354 ExpectGetManagedObjects(mock); 355 EXPECT_THROW(h.accelOobDeviceName(1), IpmiException); 356 } 357 358 TEST(HandlerTest, accelOobDeviceName_InvalidName) 359 { 360 constexpr char bad_object_path[] = "/com/google/customAccel2/bad/path"; 361 StrictMock<sdbusplus::SdBusMock> mock; 362 MockDbusHandler h(mock); 363 ExpectGetManagedObjects(mock, bad_object_path); 364 EXPECT_THROW(h.accelOobDeviceName(0), IpmiException); 365 } 366 367 constexpr uint8_t NUM_BYTES_RETURNED_EQ_NUM_BYTES = 0xff; 368 void ExpectRead(StrictMock<sdbusplus::SdBusMock>& mock, uint64_t address, 369 uint8_t num_bytes, uint64_t data, int sd_bus_call_return_value, 370 uint8_t num_bytes_returned = NUM_BYTES_RETURNED_EQ_NUM_BYTES) 371 { 372 ::testing::InSequence s; 373 374 // These must be nullptr or sd_bus_message_unref will seg fault. 375 constexpr sd_bus_message* method = nullptr; 376 constexpr sd_bus_message* msg = nullptr; 377 378 EXPECT_CALL(mock, sd_bus_message_new_method_call( 379 _, // sd_bus *bus, 380 NotNull(), // sd_bus_message **m 381 StrEq("com.google.custom_accel"), 382 StrEq("/com/google/customAccel/test/path"), 383 StrEq("com.google.custom_accel.BAR"), StrEq("Read"))) 384 .WillOnce(DoAll(SetArgPointee<1>(method), Return(0))); 385 386 EXPECT_CALL(mock, 387 sd_bus_message_append_basic( 388 method, SD_BUS_TYPE_UINT64, 389 MatcherCast<const void*>(SafeMatcherCast<const uint64_t*>( 390 Pointee(Eq(address)))))) 391 .WillOnce(Return(1)); 392 393 EXPECT_CALL(mock, 394 sd_bus_message_append_basic( 395 method, SD_BUS_TYPE_UINT64, 396 MatcherCast<const void*>(SafeMatcherCast<const uint64_t*>( 397 Pointee(Eq<uint64_t>(num_bytes)))))) 398 .WillOnce(Return(1)); 399 400 EXPECT_CALL(mock, sd_bus_call(_, // sd_bus *bus, 401 method, // sd_bus_message *m 402 _, // uint64_t timeout 403 NotNull(), // sd_bus_error *ret_error 404 NotNull())) // sd_bus_message **reply 405 .WillOnce(DoAll(SetArgPointee<3>(SD_BUS_ERROR_NULL), 406 SetArgPointee<4>(msg), // reply 407 Return(sd_bus_call_return_value))); 408 409 if (sd_bus_call_return_value >= 0) 410 { 411 if (num_bytes_returned == NUM_BYTES_RETURNED_EQ_NUM_BYTES) 412 { 413 num_bytes_returned = num_bytes; 414 } 415 416 uint64_t updatedData = 0; 417 for (size_t i = 0; i < num_bytes_returned; ++i) 418 { 419 updatedData <<= 8; 420 updatedData += (i >= 8) ? 0 : (data >> (i * 8)) & 0xff; 421 } 422 423 auto read_array_callback = 424 [updatedData, num_bytes_returned](sd_bus_message*, char, 425 const void** p, size_t* sz) { 426 *p = &updatedData; 427 *sz = num_bytes_returned; 428 }; 429 430 EXPECT_CALL(mock, sd_bus_message_read_array(nullptr, SD_BUS_TYPE_BYTE, 431 testing::_, testing::_)) 432 .WillOnce(DoAll(testing::Invoke(read_array_callback), Return(0))); 433 } 434 } 435 436 TEST(HandlerTest, accelOobRead_Success) 437 { 438 StrictMock<sdbusplus::SdBusMock> mock; 439 MockDbusHandler h(mock); 440 441 constexpr uint64_t address = 0x123456789abcdef; 442 constexpr uint8_t num_bytes = sizeof(uint64_t); 443 constexpr int sd_bus_call_return_value = 1; 444 constexpr uint64_t data = 0x13579bdf02468ace; 445 446 ExpectRead(mock, address, num_bytes, data, sd_bus_call_return_value); 447 EXPECT_EQ(data, h.accelOobRead("test/path", address, num_bytes)); 448 } 449 450 TEST(HandlerTest, accelOobRead_Fail) 451 { 452 StrictMock<sdbusplus::SdBusMock> mock; 453 MockDbusHandler h(mock); 454 455 constexpr uint64_t address = 0x123456789abcdef; 456 constexpr uint8_t num_bytes = sizeof(uint64_t); 457 constexpr int sd_bus_call_return_value = -ENOTCONN; 458 constexpr uint64_t data = 0x13579bdf02468ace; 459 460 ExpectRead(mock, address, num_bytes, data, sd_bus_call_return_value); 461 EXPECT_THROW(h.accelOobRead("test/path", address, num_bytes), 462 IpmiException); 463 } 464 465 TEST(HandlerTest, accelOobRead_TooFewBytesReturned) 466 { 467 StrictMock<sdbusplus::SdBusMock> mock; 468 MockDbusHandler h(mock); 469 470 constexpr uint64_t address = 0x123456789abcdef; 471 constexpr uint8_t num_bytes = sizeof(uint64_t); 472 constexpr int sd_bus_call_return_value = 1; 473 constexpr uint64_t data = 0x13579bdf02468ace; 474 constexpr uint8_t num_bytes_returned = num_bytes - 1; 475 476 ExpectRead(mock, address, num_bytes, data, sd_bus_call_return_value, 477 num_bytes_returned); 478 EXPECT_THROW(h.accelOobRead("test/path", address, num_bytes), 479 IpmiException); 480 } 481 482 TEST(HandlerTest, accelOobRead_TooManyBytesReturned) 483 { 484 StrictMock<sdbusplus::SdBusMock> mock; 485 MockDbusHandler h(mock); 486 487 constexpr uint64_t address = 0x123456789abcdef; 488 constexpr uint8_t num_bytes = sizeof(uint64_t); 489 constexpr int sd_bus_call_return_value = 1; 490 constexpr uint64_t data = 0x13579bdf02468ace; 491 constexpr uint8_t num_bytes_returned = sizeof(uint64_t) + 1; 492 493 ExpectRead(mock, address, num_bytes, data, sd_bus_call_return_value, 494 num_bytes_returned); 495 EXPECT_THROW(h.accelOobRead("test/path", address, num_bytes), 496 IpmiException); 497 } 498 499 static int on_array_append(sd_bus_message*, char, const void*, size_t) 500 { 501 return 0; 502 } 503 504 void ExpectWrite(StrictMock<sdbusplus::SdBusMock>& mock, uint64_t address, 505 uint8_t num_bytes, uint64_t, int sd_bus_call_return_value) 506 { 507 ::testing::InSequence s; 508 509 // These must be nullptr or sd_bus_message_unref will seg fault. 510 constexpr sd_bus_message* method = nullptr; 511 512 EXPECT_CALL(mock, sd_bus_message_new_method_call( 513 _, // sd_bus *bus, 514 NotNull(), // sd_bus_message **m 515 StrEq("com.google.custom_accel"), 516 StrEq("/com/google/customAccel/test/path"), 517 StrEq("com.google.custom_accel.BAR"), StrEq("Write"))) 518 .WillOnce(DoAll(TraceDbus("sd_bus_message_new_method_call"), 519 SetArgPointee<1>(method), Return(0))); 520 521 EXPECT_CALL(mock, 522 sd_bus_message_append_basic( 523 method, SD_BUS_TYPE_UINT64, 524 MatcherCast<const void*>(SafeMatcherCast<const uint64_t*>( 525 Pointee(Eq(address)))))) 526 .WillOnce(DoAll(TraceDbus("sd_bus_message_append_basic(address) -> 1"), 527 Return(1))); 528 529 EXPECT_CALL( 530 mock, sd_bus_message_append_array(nullptr, SD_BUS_TYPE_BYTE, testing::_, 531 num_bytes * sizeof(std::byte))) 532 .WillOnce(testing::Invoke(on_array_append)); 533 534 EXPECT_CALL(mock, sd_bus_call(_, // sd_bus *bus, 535 method, // sd_bus_message *m 536 _, // uint64_t timeout 537 NotNull(), // sd_bus_error *ret_error 538 IsNull())) // sd_bus_message **reply 539 .WillOnce(DoAll(TraceDbus("sd_bus_call() -> ret_val"), 540 SetArgPointee<3>(SD_BUS_ERROR_NULL), 541 Return(sd_bus_call_return_value))); 542 } 543 544 TEST(HandlerTest, accelOobWrite_Success) 545 { 546 StrictMock<sdbusplus::SdBusMock> mock; 547 MockDbusHandler h(mock); 548 549 constexpr uint64_t address = 0x123456789abcdef; 550 constexpr uint8_t num_bytes = sizeof(uint64_t); 551 constexpr int sd_bus_call_return_value = 1; 552 constexpr uint64_t data = 0x13579bdf02468ace; 553 554 ExpectWrite(mock, address, num_bytes, data, sd_bus_call_return_value); 555 EXPECT_NO_THROW(h.accelOobWrite("test/path", address, num_bytes, data)); 556 } 557 558 TEST(HandlerTest, accelOobRead_TooManyBytesRequested) 559 { 560 StrictMock<sdbusplus::SdBusMock> mock; 561 MockDbusHandler h(mock); 562 563 constexpr uint64_t address = 0x123456789abcdef; 564 constexpr uint8_t num_bytes = sizeof(uint64_t) + 1; 565 constexpr uint64_t data = 0x13579bdf02468ace; 566 567 EXPECT_THROW(h.accelOobWrite("test/path", address, num_bytes, data), 568 IpmiException); 569 } 570 571 TEST(HandlerTest, accelOobWrite_Fail) 572 { 573 StrictMock<sdbusplus::SdBusMock> mock; 574 MockDbusHandler h(mock); 575 576 constexpr uint64_t address = 0x123456789abcdef; 577 constexpr uint8_t num_bytes = sizeof(uint64_t); 578 constexpr int sd_bus_call_return_value = -ENOTCONN; 579 constexpr uint64_t data = 0x13579bdf02468ace; 580 581 ExpectWrite(mock, address, num_bytes, data, sd_bus_call_return_value); 582 EXPECT_THROW(h.accelOobWrite("test/path", address, num_bytes, data), 583 IpmiException); 584 } 585 586 TEST(HandlerTest, PcieBifurcation) 587 { 588 const std::string& testJson = "/tmp/test-json"; 589 auto j = R"( 590 { 591 "1": [ 1, 3 ], 592 "3": [ 3, 6 ], 593 "4": [ 3, 4, 1 ], 594 "6": [ 8 ] 595 } 596 )"_json; 597 598 std::ofstream bifurcationJson(testJson); 599 bifurcationJson << j.dump(); 600 bifurcationJson.flush(); 601 bifurcationJson.close(); 602 603 BifurcationStatic bifurcationHelper(testJson); 604 Handler h(std::ref(bifurcationHelper)); 605 606 std::unordered_map<uint8_t, std::vector<uint8_t>> expectedMapping = { 607 {1, {1, 3}}, {3, {3, 6}}, {4, {3, 4, 1}}, {6, {8}}}; 608 std::vector<uint8_t> invalidBus = {0, 2, 5, 7}; 609 610 for (const auto& [bus, output] : expectedMapping) 611 { 612 EXPECT_THAT(h.pcieBifurcation(bus), ContainerEq(output)); 613 } 614 615 for (const auto& bus : invalidBus) 616 { 617 EXPECT_TRUE(h.pcieBifurcation(bus).empty()); 618 } 619 620 std::filesystem::remove(testJson.data()); 621 bifurcationHelper = BifurcationStatic(testJson); 622 Handler h2(std::ref(bifurcationHelper)); 623 for (uint8_t i = 0; i < 8; ++i) 624 { 625 auto bifurcation = h2.pcieBifurcation(i); 626 EXPECT_TRUE(bifurcation.empty()); 627 } 628 } 629 630 TEST(HandlerTest, BmInstanceFailCase) 631 { 632 StrictMock<sdbusplus::SdBusMock> mock; 633 MockDbusHandler h(mock); 634 635 // Invalid enum 636 EXPECT_THROW(h.getBMInstanceProperty(0x07), IpmiException); 637 638 // Valid enum but no path exists 639 EXPECT_THROW(h.getBMInstanceProperty(0x00), IpmiException); 640 } 641 642 TEST(HandlerTest, GetCoreCountFileDoesNotExist) 643 { 644 Handler h; 645 EXPECT_EQ(h.getCoreCount("non_existent_file.json"), std::nullopt); 646 } 647 648 TEST(HandlerTest, GetCoreCountValidFile) 649 { 650 const char* testFilename = "cpu_config_valid.json"; 651 std::string contents = R"({"cpu_core_count": 64})"; 652 std::ofstream outputJson(testFilename); 653 outputJson << contents; 654 outputJson.close(); 655 656 Handler h; 657 EXPECT_EQ(h.getCoreCount(testFilename), 64); 658 std::remove(testFilename); 659 } 660 661 TEST(HandlerTest, GetCoreCountEmptyFile) 662 { 663 const char* testFilename = "cpu_config_empty.json"; 664 std::ofstream outputJson(testFilename); 665 outputJson.close(); 666 667 Handler h; 668 EXPECT_EQ(h.getCoreCount(testFilename), std::nullopt); 669 std::remove(testFilename); 670 } 671 672 TEST(HandlerTest, GetCoreCountInvalidJson) 673 { 674 const char* testFilename = "cpu_config_invalid.json"; 675 std::string contents = R"({"cpu_core_count": "not an int"})"; 676 std::ofstream outputJson(testFilename); 677 outputJson << contents; 678 outputJson.close(); 679 680 Handler h; 681 EXPECT_EQ(h.getCoreCount(testFilename), std::nullopt); 682 std::remove(testFilename); 683 } 684 685 TEST(HandlerTest, GetCoreCountMissingKey) 686 { 687 const char* testFilename = "cpu_config_missing_key.json"; 688 std::string contents = R"({"other_key": 12})"; 689 std::ofstream outputJson(testFilename); 690 outputJson << contents; 691 outputJson.close(); 692 693 Handler h; 694 EXPECT_EQ(h.getCoreCount(testFilename), std::nullopt); 695 std::remove(testFilename); 696 } 697 698 TEST(HandlerTest, GetCoreCountOutOfRange) 699 { 700 const char* testFilename = "cpu_config_range.json"; 701 std::string contents = R"({"cpu_core_count": 70000})"; 702 std::ofstream outputJson(testFilename); 703 outputJson << contents; 704 outputJson.close(); 705 706 Handler h; 707 EXPECT_EQ(h.getCoreCount(testFilename), std::nullopt); 708 std::remove(testFilename); 709 } 710 711 TEST(HandlerTest, GetCoreCountWithExtraKeys) 712 { 713 const char* testFilename = "cpu_config_extra_keys.json"; 714 std::string contents = 715 R"({"other_key": "some_value", "cpu_core_count": 32, "another_key": 123})"; 716 std::ofstream outputJson(testFilename); 717 outputJson << contents; 718 outputJson.close(); 719 720 Handler h; 721 EXPECT_EQ(h.getCoreCount(testFilename), 32); 722 std::remove(testFilename); 723 } 724 725 // TODO: Add checks for other functions of handler. 726 727 } // namespace ipmi 728 } // namespace google 729