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