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