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 <fstream> 22 #include <nlohmann/json.hpp> 23 #include <sdbusplus/message.hpp> 24 #include <sdbusplus/test/sdbus_mock.hpp> 25 #include <string> 26 #include <tuple> 27 28 #include <gtest/gtest.h> 29 30 namespace google 31 { 32 namespace ipmi 33 { 34 35 TEST(HandlerTest, EthCheckValidHappy) 36 { 37 Handler h; 38 std::tuple<std::uint8_t, std::string> result = h.getEthDetails("et"); 39 EXPECT_EQ(12, std::get<0>(result)); 40 EXPECT_STREQ("et", std::get<1>(result).c_str()); 41 } 42 43 TEST(HandlerTest, CableCheckIllegalPath) 44 { 45 Handler h; 46 EXPECT_THROW(h.getRxPackets("eth0/../../"), IpmiException); 47 } 48 49 TEST(HandlerTest, readNameFromConfigInstanceVariety) 50 { 51 // Make sure it handles the failures and successes as we expect. 52 struct testCase 53 { 54 std::string type; 55 std::uint8_t instance; 56 std::string expectedName; 57 }; 58 59 std::vector<testCase> tests = { 60 {"cpu", 5, ""}, 61 {"cpu", 3, "CPU2"}, 62 }; 63 64 auto j2 = R"( 65 { 66 "cpu": [ 67 {"instance": 1, "name": "CPU0"}, 68 {"instance": 2, "name": "CPU1"}, 69 {"instance": 3, "name": "CPU2"}, 70 {"instance": 4, "name": "CPU3"} 71 ] 72 } 73 )"_json; 74 75 for (const auto& test : tests) 76 { 77 EXPECT_STREQ(test.expectedName.c_str(), 78 readNameFromConfig(test.type, test.instance, j2).c_str()); 79 } 80 } 81 82 // TODO: If we can test with phosphor-logging in the future, there are more 83 // failure cases. 84 85 TEST(HandlerTest, getEntityNameWithNameNotFoundExcepts) 86 { 87 const char* testFilename = "test.json"; 88 std::string contents = R"({"cpu": [{"instance": 1, "name": "CPU0"}]})"; 89 std::ofstream outputJson(testFilename); 90 outputJson << contents; 91 outputJson.flush(); 92 outputJson.close(); 93 94 Handler h(testFilename); 95 EXPECT_THROW(h.getEntityName(0x03, 2), IpmiException); 96 (void)std::remove(testFilename); 97 } 98 99 TEST(HandlerTest, getEntityNameWithNameFoundReturnsIt) 100 { 101 const char* testFilename = "test.json"; 102 std::string contents = R"({"cpu": [{"instance": 1, "name": "CPU0"}]})"; 103 std::ofstream outputJson(testFilename); 104 outputJson << contents; 105 outputJson.flush(); 106 outputJson.close(); 107 108 Handler h(testFilename); 109 EXPECT_STREQ("CPU0", h.getEntityName(0x03, 1).c_str()); 110 (void)std::remove(testFilename); 111 } 112 113 using ::testing::_; 114 using ::testing::AnyNumber; 115 using ::testing::DoAll; 116 using ::testing::ElementsAre; 117 using ::testing::Eq; 118 using ::testing::IsNull; 119 using ::testing::MatcherCast; 120 using ::testing::NotNull; 121 using ::testing::Pointee; 122 using ::testing::Return; 123 using ::testing::ReturnNull; 124 using ::testing::SafeMatcherCast; 125 using ::testing::SetArgPointee; 126 using ::testing::StrEq; 127 using ::testing::StrictMock; 128 using ::testing::StrNe; 129 using ::testing::WithArg; 130 131 class MockDbusHandler : public Handler 132 { 133 public: 134 MockDbusHandler(sdbusplus::SdBusMock& mock, 135 const std::string& config = "") : 136 Handler(config), 137 mock_(&mock) 138 { 139 } 140 141 protected: 142 sdbusplus::bus::bus accelOobGetDbus() const override 143 { 144 return sdbusplus::get_mocked_new( 145 const_cast<sdbusplus::SdBusMock*>(mock_)); 146 } 147 148 private: 149 sdbusplus::SdBusMock* mock_; 150 }; 151 152 ACTION_TEMPLATE(AssignReadVal, HAS_1_TEMPLATE_PARAMS(typename, T), 153 AND_1_VALUE_PARAMS(val)) 154 { 155 *static_cast<T*>(arg2) = val; 156 } 157 158 ACTION_P(TraceDbus, msg) 159 { 160 std::fprintf(stderr, "%s\n", msg); 161 } 162 163 ACTION_P(TraceDbus2, msg) 164 { 165 std::fprintf(stderr, "%s(%02x)\n", msg, *static_cast<const uint8_t*>(arg2)); 166 } 167 168 constexpr char object_path[] = "/com/google/customAccel/test/path"; 169 constexpr char property_grpc[] = "com.google.custom_accel.gRPC"; 170 constexpr char value_port[] = "Port"; 171 constexpr uint32_t port = 5000; 172 173 constexpr char SD_BUS_TYPE_BYTE_STR[] = {SD_BUS_TYPE_BYTE, '\0'}; 174 175 // Returns an object that looks like: 176 // "/com/google/customAccel/test/path": { 177 // "com.google.custom_accel.gRPC" : { 178 // "Port" : { 179 // "type" : "u", 180 // "data" : 5000 181 // } 182 // } 183 // } 184 void ExpectGetManagedObjects(StrictMock<sdbusplus::SdBusMock>& mock, 185 const char* obj_path = object_path) 186 { 187 ::testing::InSequence s; 188 189 // These must be nullptr or sd_bus_message_unref will seg fault. 190 constexpr sd_bus_message* method = nullptr; 191 constexpr sd_bus_message* msg = nullptr; 192 193 EXPECT_CALL(mock, sd_bus_message_new_method_call( 194 _, // sd_bus *bus, 195 NotNull(), // sd_bus_message **m 196 StrEq("com.google.custom_accel"), StrEq("/"), 197 StrEq("org.freedesktop.DBus.ObjectManager"), 198 StrEq("GetManagedObjects"))) 199 .WillOnce(DoAll(SetArgPointee<1>(method), Return(0))); 200 201 EXPECT_CALL(mock, sd_bus_call(_, // sd_bus *bus, 202 method, // sd_bus_message *m 203 _, // uint64_t timeout 204 NotNull(), // sd_bus_error *ret_error 205 NotNull())) // sd_bus_message **reply 206 .WillOnce(DoAll(SetArgPointee<3>(SD_BUS_ERROR_NULL), 207 SetArgPointee<4>(msg), // reply 208 Return(0))); 209 210 EXPECT_CALL(mock, sd_bus_message_enter_container(msg, SD_BUS_TYPE_ARRAY, 211 StrEq("{oa{sa{sv}}}"))) 212 .WillOnce(Return(1)); 213 214 EXPECT_CALL(mock, sd_bus_message_at_end(msg, 0)).WillOnce(Return(0)); 215 216 EXPECT_CALL(mock, sd_bus_message_enter_container( 217 msg, SD_BUS_TYPE_DICT_ENTRY, StrEq("oa{sa{sv}}"))) 218 .WillOnce(Return(1)); 219 220 EXPECT_CALL(mock, sd_bus_message_read_basic(msg, SD_BUS_TYPE_OBJECT_PATH, 221 NotNull())) 222 .WillOnce(DoAll(AssignReadVal<const char*>(obj_path), Return(1))); 223 224 EXPECT_CALL(mock, sd_bus_message_enter_container(msg, SD_BUS_TYPE_ARRAY, 225 StrEq("{sa{sv}}"))) 226 .WillOnce(Return(1)); 227 228 EXPECT_CALL(mock, sd_bus_message_at_end(msg, 0)).WillOnce(Return(0)); 229 230 EXPECT_CALL(mock, sd_bus_message_enter_container( 231 msg, SD_BUS_TYPE_DICT_ENTRY, StrEq("sa{sv}"))) 232 .WillOnce(Return(1)); 233 234 EXPECT_CALL(mock, 235 sd_bus_message_read_basic(msg, SD_BUS_TYPE_STRING, NotNull())) 236 .WillOnce(DoAll(AssignReadVal<const char*>(property_grpc), Return(1))); 237 238 EXPECT_CALL(mock, sd_bus_message_enter_container(msg, SD_BUS_TYPE_ARRAY, 239 StrEq("{sv}"))) 240 .WillOnce(Return(1)); 241 242 EXPECT_CALL(mock, sd_bus_message_at_end(msg, 0)).WillOnce(Return(0)); 243 244 EXPECT_CALL(mock, sd_bus_message_enter_container( 245 msg, SD_BUS_TYPE_DICT_ENTRY, StrEq("sv"))) 246 .WillOnce(Return(1)); 247 248 EXPECT_CALL(mock, 249 sd_bus_message_read_basic(msg, SD_BUS_TYPE_STRING, NotNull())) 250 .WillOnce(DoAll(AssignReadVal<const char*>(value_port), Return(1))); 251 252 EXPECT_CALL( 253 mock, sd_bus_message_verify_type(msg, SD_BUS_TYPE_VARIANT, StrNe("u"))) 254 .Times(AnyNumber()) 255 .WillRepeatedly(Return(0)); 256 257 EXPECT_CALL( 258 mock, sd_bus_message_verify_type(msg, SD_BUS_TYPE_VARIANT, StrEq("u"))) 259 .WillOnce(Return(1)); 260 261 EXPECT_CALL(mock, sd_bus_message_enter_container(msg, SD_BUS_TYPE_VARIANT, 262 StrEq("u"))) 263 .WillOnce(Return(1)); 264 265 EXPECT_CALL(mock, 266 sd_bus_message_read_basic(msg, SD_BUS_TYPE_UINT32, NotNull())) 267 .WillOnce(DoAll(AssignReadVal<uint32_t>(port), Return(0))); 268 269 EXPECT_CALL( 270 mock, sd_bus_message_verify_type(msg, SD_BUS_TYPE_VARIANT, StrNe("u"))) 271 .Times(AnyNumber()) 272 .WillRepeatedly(Return(0)); 273 274 EXPECT_CALL(mock, sd_bus_message_exit_container(msg)) 275 .WillOnce(Return(1)) 276 .WillOnce(Return(1)); 277 278 EXPECT_CALL(mock, sd_bus_message_at_end(msg, 0)).WillOnce(Return(1)); 279 280 EXPECT_CALL(mock, sd_bus_message_exit_container(msg)) 281 .WillOnce(Return(1)) 282 .WillOnce(Return(1)); 283 284 EXPECT_CALL(mock, sd_bus_message_at_end(msg, 0)).WillOnce(Return(1)); 285 286 EXPECT_CALL(mock, sd_bus_message_exit_container(msg)) 287 .WillOnce(Return(1)) 288 .WillOnce(Return(1)); 289 290 EXPECT_CALL(mock, sd_bus_message_at_end(msg, 0)).WillOnce(Return(1)); 291 292 EXPECT_CALL(mock, sd_bus_message_exit_container(msg)).WillOnce(Return(1)); 293 } 294 295 void ExpectSdBusError(StrictMock<sdbusplus::SdBusMock>& mock) 296 { 297 EXPECT_CALL(mock, sd_bus_message_new_method_call( 298 _, // sd_bus *bus, 299 NotNull(), // sd_bus_message **m 300 StrEq("com.google.custom_accel"), StrEq("/"), 301 StrEq("org.freedesktop.DBus.ObjectManager"), 302 StrEq("GetManagedObjects"))) 303 .WillOnce(Return(-ENOTCONN)); 304 } 305 306 TEST(HandlerTest, accelOobDeviceCount_Success) 307 { 308 StrictMock<sdbusplus::SdBusMock> mock; 309 MockDbusHandler h(mock); 310 ExpectGetManagedObjects(mock); 311 EXPECT_EQ(1, h.accelOobDeviceCount()); 312 } 313 314 TEST(HandlerTest, accelOobDeviceCount_Fail) 315 { 316 StrictMock<sdbusplus::SdBusMock> mock; 317 MockDbusHandler h(mock); 318 ExpectSdBusError(mock); 319 EXPECT_THROW(h.accelOobDeviceCount(), IpmiException); 320 } 321 322 TEST(HandlerTest, accelOobDeviceName_Success) 323 { 324 StrictMock<sdbusplus::SdBusMock> mock; 325 MockDbusHandler h(mock); 326 ExpectGetManagedObjects(mock); 327 EXPECT_EQ(std::string("test/path"), h.accelOobDeviceName(0)); 328 } 329 330 TEST(HandlerTest, accelOobDeviceName_Fail) 331 { 332 StrictMock<sdbusplus::SdBusMock> mock; 333 MockDbusHandler h(mock); 334 ExpectSdBusError(mock); 335 EXPECT_THROW(h.accelOobDeviceName(0), IpmiException); 336 } 337 338 TEST(HandlerTest, accelOobDeviceName_OutOfRange) 339 { 340 StrictMock<sdbusplus::SdBusMock> mock; 341 MockDbusHandler h(mock); 342 ExpectGetManagedObjects(mock); 343 EXPECT_THROW(h.accelOobDeviceName(1), IpmiException); 344 } 345 346 TEST(HandlerTest, accelOobDeviceName_InvalidName) 347 { 348 constexpr char bad_object_path[] = "/com/google/customAccel2/bad/path"; 349 StrictMock<sdbusplus::SdBusMock> mock; 350 MockDbusHandler h(mock); 351 ExpectGetManagedObjects(mock, bad_object_path); 352 EXPECT_THROW(h.accelOobDeviceName(0), IpmiException); 353 } 354 355 constexpr uint8_t NUM_BYTES_RETURNED_EQ_NUM_BYTES = 0xff; 356 void ExpectRead(StrictMock<sdbusplus::SdBusMock>& mock, uint64_t address, 357 uint8_t num_bytes, uint64_t data, int sd_bus_call_return_value, 358 uint8_t num_bytes_returned = NUM_BYTES_RETURNED_EQ_NUM_BYTES) 359 { 360 ::testing::InSequence s; 361 362 // These must be nullptr or sd_bus_message_unref will seg fault. 363 constexpr sd_bus_message* method = nullptr; 364 constexpr sd_bus_message* msg = nullptr; 365 366 EXPECT_CALL(mock, sd_bus_message_new_method_call( 367 _, // sd_bus *bus, 368 NotNull(), // sd_bus_message **m 369 StrEq("com.google.custom_accel"), 370 StrEq("/com/google/customAccel/test/path"), 371 StrEq("com.google.custom_accel.BAR"), StrEq("Read"))) 372 .WillOnce(DoAll(SetArgPointee<1>(method), Return(0))); 373 374 EXPECT_CALL( 375 mock, sd_bus_message_append_basic( 376 method, SD_BUS_TYPE_UINT64, 377 MatcherCast<const void*>( 378 SafeMatcherCast<const uint64_t*>(Pointee(Eq(address)))))) 379 .WillOnce(Return(1)); 380 381 EXPECT_CALL(mock, 382 sd_bus_message_append_basic( 383 method, SD_BUS_TYPE_UINT64, 384 MatcherCast<const void*>(SafeMatcherCast<const uint64_t*>( 385 Pointee(Eq<uint64_t>(num_bytes)))))) 386 .WillOnce(Return(1)); 387 388 EXPECT_CALL(mock, sd_bus_call(_, // sd_bus *bus, 389 method, // sd_bus_message *m 390 _, // uint64_t timeout 391 NotNull(), // sd_bus_error *ret_error 392 NotNull())) // sd_bus_message **reply 393 .WillOnce(DoAll(SetArgPointee<3>(SD_BUS_ERROR_NULL), 394 SetArgPointee<4>(msg), // reply 395 Return(sd_bus_call_return_value))); 396 397 if (sd_bus_call_return_value >= 0) 398 { 399 EXPECT_CALL(mock, 400 sd_bus_message_enter_container(msg, SD_BUS_TYPE_ARRAY, 401 StrEq(SD_BUS_TYPE_BYTE_STR))) 402 .WillOnce(Return(1)); 403 404 if (num_bytes_returned == NUM_BYTES_RETURNED_EQ_NUM_BYTES) 405 { 406 num_bytes_returned = num_bytes; 407 } 408 for (auto i = num_bytes_returned - 1; i >= 0; --i) 409 { 410 EXPECT_CALL(mock, sd_bus_message_at_end(msg, 0)) 411 .WillOnce(Return(0)); 412 413 const uint8_t byte = (data >> (8 * i)) & 0xff; 414 EXPECT_CALL(mock, sd_bus_message_read_basic(msg, SD_BUS_TYPE_BYTE, 415 NotNull())) 416 .WillOnce(DoAll(AssignReadVal<uint8_t>(byte), Return(1))); 417 } 418 419 EXPECT_CALL(mock, sd_bus_message_at_end(msg, 0)).WillOnce(Return(1)); 420 421 EXPECT_CALL(mock, sd_bus_message_exit_container(msg)) 422 .WillOnce(Return(1)); 423 } 424 } 425 426 TEST(HandlerTest, accelOobRead_Success) 427 { 428 StrictMock<sdbusplus::SdBusMock> mock; 429 MockDbusHandler h(mock); 430 431 constexpr uint64_t address = 0x123456789abcdef; 432 constexpr uint8_t num_bytes = sizeof(uint64_t); 433 constexpr int sd_bus_call_return_value = 1; 434 constexpr uint64_t data = 0x13579bdf02468ace; 435 436 ExpectRead(mock, address, num_bytes, data, sd_bus_call_return_value); 437 EXPECT_EQ(data, h.accelOobRead("test/path", address, num_bytes)); 438 } 439 440 TEST(HandlerTest, accelOobRead_Fail) 441 { 442 StrictMock<sdbusplus::SdBusMock> mock; 443 MockDbusHandler h(mock); 444 445 constexpr uint64_t address = 0x123456789abcdef; 446 constexpr uint8_t num_bytes = sizeof(uint64_t); 447 constexpr int sd_bus_call_return_value = -ENOTCONN; 448 constexpr uint64_t data = 0x13579bdf02468ace; 449 450 ExpectRead(mock, address, num_bytes, data, sd_bus_call_return_value); 451 EXPECT_THROW(h.accelOobRead("test/path", address, num_bytes), 452 IpmiException); 453 } 454 455 TEST(HandlerTest, accelOobRead_TooFewBytesReturned) 456 { 457 StrictMock<sdbusplus::SdBusMock> mock; 458 MockDbusHandler h(mock); 459 460 constexpr uint64_t address = 0x123456789abcdef; 461 constexpr uint8_t num_bytes = sizeof(uint64_t); 462 constexpr int sd_bus_call_return_value = 1; 463 constexpr uint64_t data = 0x13579bdf02468ace; 464 constexpr uint8_t num_bytes_returned = num_bytes - 1; 465 466 ExpectRead(mock, address, num_bytes, data, sd_bus_call_return_value, 467 num_bytes_returned); 468 EXPECT_THROW(h.accelOobRead("test/path", address, num_bytes), 469 IpmiException); 470 } 471 472 TEST(HandlerTest, accelOobRead_TooManyBytesReturned) 473 { 474 StrictMock<sdbusplus::SdBusMock> mock; 475 MockDbusHandler h(mock); 476 477 constexpr uint64_t address = 0x123456789abcdef; 478 constexpr uint8_t num_bytes = sizeof(uint64_t); 479 constexpr int sd_bus_call_return_value = 1; 480 constexpr uint64_t data = 0x13579bdf02468ace; 481 constexpr uint8_t num_bytes_returned = sizeof(uint64_t) + 1; 482 483 ExpectRead(mock, address, num_bytes, data, sd_bus_call_return_value, 484 num_bytes_returned); 485 EXPECT_THROW(h.accelOobRead("test/path", address, num_bytes), 486 IpmiException); 487 } 488 489 void ExpectWrite(StrictMock<sdbusplus::SdBusMock>& mock, uint64_t address, 490 uint8_t num_bytes, uint64_t data, int sd_bus_call_return_value) 491 { 492 ::testing::InSequence s; 493 494 // These must be nullptr or sd_bus_message_unref will seg fault. 495 constexpr sd_bus_message* method = nullptr; 496 497 EXPECT_CALL(mock, sd_bus_message_new_method_call( 498 _, // sd_bus *bus, 499 NotNull(), // sd_bus_message **m 500 StrEq("com.google.custom_accel"), 501 StrEq("/com/google/customAccel/test/path"), 502 StrEq("com.google.custom_accel.BAR"), StrEq("Write"))) 503 .WillOnce(DoAll(TraceDbus("sd_bus_message_new_method_call"), 504 SetArgPointee<1>(method), Return(0))); 505 506 EXPECT_CALL( 507 mock, sd_bus_message_append_basic( 508 method, SD_BUS_TYPE_UINT64, 509 MatcherCast<const void*>( 510 SafeMatcherCast<const uint64_t*>(Pointee(Eq(address)))))) 511 .WillOnce(DoAll(TraceDbus("sd_bus_message_append_basic(address) -> 1"), 512 Return(1))); 513 514 EXPECT_CALL(mock, 515 sd_bus_message_open_container(method, SD_BUS_TYPE_ARRAY, 516 StrEq(SD_BUS_TYPE_BYTE_STR))) 517 .WillOnce(DoAll(TraceDbus("sd_bus_message_open_container(a, y) -> 0"), 518 Return(0))); 519 520 for (auto i = 0; i < num_bytes; ++i) 521 { 522 const uint8_t byte = (data >> (8 * i)) & 0xff; 523 524 EXPECT_CALL( 525 mock, sd_bus_message_append_basic( 526 method, SD_BUS_TYPE_BYTE, 527 MatcherCast<const void*>( 528 SafeMatcherCast<const uint8_t*>(Pointee(Eq(byte)))))) 529 .WillOnce( 530 DoAll(TraceDbus2("sd_bus_message_append_basic"), Return(1))); 531 } 532 533 EXPECT_CALL(mock, sd_bus_message_close_container(method)) 534 .WillOnce(DoAll(TraceDbus("sd_bus_message_close_container() -> 0"), 535 Return(0))); 536 537 EXPECT_CALL(mock, sd_bus_call(_, // sd_bus *bus, 538 method, // sd_bus_message *m 539 _, // uint64_t timeout 540 NotNull(), // sd_bus_error *ret_error 541 IsNull())) // sd_bus_message **reply 542 .WillOnce(DoAll(TraceDbus("sd_bus_call() -> ret_val"), 543 SetArgPointee<3>(SD_BUS_ERROR_NULL), 544 Return(sd_bus_call_return_value))); 545 } 546 547 TEST(HandlerTest, accelOobWrite_Success) 548 { 549 StrictMock<sdbusplus::SdBusMock> mock; 550 MockDbusHandler h(mock); 551 552 constexpr uint64_t address = 0x123456789abcdef; 553 constexpr uint8_t num_bytes = sizeof(uint64_t); 554 constexpr int sd_bus_call_return_value = 1; 555 constexpr uint64_t data = 0x13579bdf02468ace; 556 557 ExpectWrite(mock, address, num_bytes, data, sd_bus_call_return_value); 558 EXPECT_NO_THROW(h.accelOobWrite("test/path", address, num_bytes, data)); 559 } 560 561 TEST(HandlerTest, accelOobRead_TooManyBytesRequested) 562 { 563 StrictMock<sdbusplus::SdBusMock> mock; 564 MockDbusHandler h(mock); 565 566 constexpr uint64_t address = 0x123456789abcdef; 567 constexpr uint8_t num_bytes = sizeof(uint64_t) + 1; 568 constexpr uint64_t data = 0x13579bdf02468ace; 569 570 EXPECT_THROW(h.accelOobWrite("test/path", address, num_bytes, data), 571 IpmiException); 572 } 573 574 TEST(HandlerTest, accelOobWrite_Fail) 575 { 576 StrictMock<sdbusplus::SdBusMock> mock; 577 MockDbusHandler h(mock); 578 579 constexpr uint64_t address = 0x123456789abcdef; 580 constexpr uint8_t num_bytes = sizeof(uint64_t); 581 constexpr int sd_bus_call_return_value = -ENOTCONN; 582 constexpr uint64_t data = 0x13579bdf02468ace; 583 584 ExpectWrite(mock, address, num_bytes, data, sd_bus_call_return_value); 585 EXPECT_THROW(h.accelOobWrite("test/path", address, num_bytes, data), 586 IpmiException); 587 } 588 589 // TODO: Add checks for other functions of handler. 590 591 } // namespace ipmi 592 } // namespace google 593