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 <string>
31 #include <tuple>
32 
33 #include <gtest/gtest.h>
34 
35 namespace google
36 {
37 namespace ipmi
38 {
39 
40 using testing::_;
41 using testing::Return;
42 
43 TEST(HandlerTest, EthCheckValidHappy)
44 {
45     Handler h;
46     std::tuple<std::uint8_t, std::string> result = h.getEthDetails("et");
47     EXPECT_EQ(12, std::get<0>(result));
48     EXPECT_STREQ("et", std::get<1>(result).c_str());
49 }
50 
51 TEST(HandlerTest, CableCheckIllegalPath)
52 {
53     Handler h;
54     EXPECT_THROW(h.getRxPackets("eth0/../../"), IpmiException);
55 }
56 
57 TEST(HandlerTest, readNameFromConfigInstanceVariety)
58 {
59     // Make sure it handles the failures and successes as we expect.
60     struct testCase
61     {
62         std::string type;
63         std::uint8_t instance;
64         std::string expectedName;
65     };
66 
67     std::vector<testCase> tests = {
68         {"cpu", 5, ""},
69         {"cpu", 3, "CPU2"},
70     };
71 
72     auto j2 = R"(
73       {
74         "cpu": [
75           {"instance": 1, "name": "CPU0"},
76           {"instance": 2, "name": "CPU1"},
77           {"instance": 3, "name": "CPU2"},
78           {"instance": 4, "name": "CPU3"}
79         ]
80       }
81     )"_json;
82 
83     for (const auto& test : tests)
84     {
85         EXPECT_STREQ(test.expectedName.c_str(),
86                      readNameFromConfig(test.type, test.instance, j2).c_str());
87     }
88 }
89 
90 // TODO: If we can test with phosphor-logging in the future, there are more
91 // failure cases.
92 
93 TEST(HandlerTest, getEntityNameWithNameNotFoundExcepts)
94 {
95     const char* testFilename = "test.json";
96     std::string contents = R"({"cpu": [{"instance": 1, "name": "CPU0"}]})";
97     std::ofstream outputJson(testFilename);
98     outputJson << contents;
99     outputJson.flush();
100     outputJson.close();
101 
102     Handler h(testFilename);
103     EXPECT_THROW(h.getEntityName(0x03, 2), IpmiException);
104     (void)std::remove(testFilename);
105 }
106 
107 TEST(HandlerTest, getEntityNameWithNameFoundReturnsIt)
108 {
109     const char* testFilename = "test.json";
110     std::string contents = R"({"cpu": [{"instance": 1, "name": "CPU0"}]})";
111     std::ofstream outputJson(testFilename);
112     outputJson << contents;
113     outputJson.flush();
114     outputJson.close();
115 
116     Handler h(testFilename);
117     EXPECT_STREQ("CPU0", h.getEntityName(0x03, 1).c_str());
118     (void)std::remove(testFilename);
119 }
120 
121 using ::testing::_;
122 using ::testing::AnyNumber;
123 using ::testing::ContainerEq;
124 using ::testing::DoAll;
125 using ::testing::ElementsAre;
126 using ::testing::Eq;
127 using ::testing::IsNull;
128 using ::testing::MatcherCast;
129 using ::testing::NotNull;
130 using ::testing::Pointee;
131 using ::testing::Return;
132 using ::testing::ReturnNull;
133 using ::testing::SafeMatcherCast;
134 using ::testing::SetArgPointee;
135 using ::testing::StrEq;
136 using ::testing::StrictMock;
137 using ::testing::StrNe;
138 using ::testing::WithArg;
139 
140 class MockDbusHandler : public Handler
141 {
142   public:
143     MockDbusHandler(sdbusplus::SdBusMock& mock,
144                     const std::string& config = "") :
145         Handler(config),
146         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(
309         mock, sd_bus_message_new_method_call(_,         // sd_bus *bus,
310                                              NotNull(), // sd_bus_message **m
311                                              StrEq(service), StrEq(objPath),
312                                              StrEq(interface), 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(
387         mock, sd_bus_message_append_basic(
388                   method, SD_BUS_TYPE_UINT64,
389                   MatcherCast<const void*>(
390                       SafeMatcherCast<const uint64_t*>(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         EXPECT_CALL(mock,
412                     sd_bus_message_enter_container(msg, SD_BUS_TYPE_ARRAY,
413                                                    StrEq(SD_BUS_TYPE_BYTE_STR)))
414             .WillOnce(Return(1));
415 
416         if (num_bytes_returned == NUM_BYTES_RETURNED_EQ_NUM_BYTES)
417         {
418             num_bytes_returned = num_bytes;
419         }
420         for (auto i = num_bytes_returned - 1; i >= 0; --i)
421         {
422             EXPECT_CALL(mock, sd_bus_message_at_end(msg, 0))
423                 .WillOnce(Return(0));
424 
425             const uint8_t byte = (i >= 8) ? 0 : (data >> (8 * i)) & 0xff;
426             EXPECT_CALL(mock, sd_bus_message_read_basic(msg, SD_BUS_TYPE_BYTE,
427                                                         NotNull()))
428                 .WillOnce(DoAll(AssignReadVal<uint8_t>(byte), Return(1)));
429         }
430 
431         EXPECT_CALL(mock, sd_bus_message_at_end(msg, 0)).WillOnce(Return(1));
432 
433         EXPECT_CALL(mock, sd_bus_message_exit_container(msg))
434             .WillOnce(Return(1));
435     }
436 }
437 
438 TEST(HandlerTest, accelOobRead_Success)
439 {
440     StrictMock<sdbusplus::SdBusMock> mock;
441     MockDbusHandler h(mock);
442 
443     constexpr uint64_t address = 0x123456789abcdef;
444     constexpr uint8_t num_bytes = sizeof(uint64_t);
445     constexpr int sd_bus_call_return_value = 1;
446     constexpr uint64_t data = 0x13579bdf02468ace;
447 
448     ExpectRead(mock, address, num_bytes, data, sd_bus_call_return_value);
449     EXPECT_EQ(data, h.accelOobRead("test/path", address, num_bytes));
450 }
451 
452 TEST(HandlerTest, accelOobRead_Fail)
453 {
454     StrictMock<sdbusplus::SdBusMock> mock;
455     MockDbusHandler h(mock);
456 
457     constexpr uint64_t address = 0x123456789abcdef;
458     constexpr uint8_t num_bytes = sizeof(uint64_t);
459     constexpr int sd_bus_call_return_value = -ENOTCONN;
460     constexpr uint64_t data = 0x13579bdf02468ace;
461 
462     ExpectRead(mock, address, num_bytes, data, sd_bus_call_return_value);
463     EXPECT_THROW(h.accelOobRead("test/path", address, num_bytes),
464                  IpmiException);
465 }
466 
467 TEST(HandlerTest, accelOobRead_TooFewBytesReturned)
468 {
469     StrictMock<sdbusplus::SdBusMock> mock;
470     MockDbusHandler h(mock);
471 
472     constexpr uint64_t address = 0x123456789abcdef;
473     constexpr uint8_t num_bytes = sizeof(uint64_t);
474     constexpr int sd_bus_call_return_value = 1;
475     constexpr uint64_t data = 0x13579bdf02468ace;
476     constexpr uint8_t num_bytes_returned = num_bytes - 1;
477 
478     ExpectRead(mock, address, num_bytes, data, sd_bus_call_return_value,
479                num_bytes_returned);
480     EXPECT_THROW(h.accelOobRead("test/path", address, num_bytes),
481                  IpmiException);
482 }
483 
484 TEST(HandlerTest, accelOobRead_TooManyBytesReturned)
485 {
486     StrictMock<sdbusplus::SdBusMock> mock;
487     MockDbusHandler h(mock);
488 
489     constexpr uint64_t address = 0x123456789abcdef;
490     constexpr uint8_t num_bytes = sizeof(uint64_t);
491     constexpr int sd_bus_call_return_value = 1;
492     constexpr uint64_t data = 0x13579bdf02468ace;
493     constexpr uint8_t num_bytes_returned = sizeof(uint64_t) + 1;
494 
495     ExpectRead(mock, address, num_bytes, data, sd_bus_call_return_value,
496                num_bytes_returned);
497     EXPECT_THROW(h.accelOobRead("test/path", address, num_bytes),
498                  IpmiException);
499 }
500 
501 void ExpectWrite(StrictMock<sdbusplus::SdBusMock>& mock, uint64_t address,
502                  uint8_t num_bytes, uint64_t data, int sd_bus_call_return_value)
503 {
504     ::testing::InSequence s;
505 
506     // These must be nullptr or sd_bus_message_unref will seg fault.
507     constexpr sd_bus_message* method = nullptr;
508 
509     EXPECT_CALL(mock, sd_bus_message_new_method_call(
510                           _,         // sd_bus *bus,
511                           NotNull(), // sd_bus_message **m
512                           StrEq("com.google.custom_accel"),
513                           StrEq("/com/google/customAccel/test/path"),
514                           StrEq("com.google.custom_accel.BAR"), StrEq("Write")))
515         .WillOnce(DoAll(TraceDbus("sd_bus_message_new_method_call"),
516                         SetArgPointee<1>(method), Return(0)));
517 
518     EXPECT_CALL(
519         mock, sd_bus_message_append_basic(
520                   method, SD_BUS_TYPE_UINT64,
521                   MatcherCast<const void*>(
522                       SafeMatcherCast<const uint64_t*>(Pointee(Eq(address))))))
523         .WillOnce(DoAll(TraceDbus("sd_bus_message_append_basic(address) -> 1"),
524                         Return(1)));
525 
526     EXPECT_CALL(mock,
527                 sd_bus_message_open_container(method, SD_BUS_TYPE_ARRAY,
528                                               StrEq(SD_BUS_TYPE_BYTE_STR)))
529         .WillOnce(DoAll(TraceDbus("sd_bus_message_open_container(a, y) -> 0"),
530                         Return(0)));
531 
532     for (auto i = 0; i < num_bytes; ++i)
533     {
534         const uint8_t byte = (data >> (8 * i)) & 0xff;
535 
536         EXPECT_CALL(
537             mock, sd_bus_message_append_basic(
538                       method, SD_BUS_TYPE_BYTE,
539                       MatcherCast<const void*>(
540                           SafeMatcherCast<const uint8_t*>(Pointee(Eq(byte))))))
541             .WillOnce(
542                 DoAll(TraceDbus2("sd_bus_message_append_basic"), Return(1)));
543     }
544 
545     EXPECT_CALL(mock, sd_bus_message_close_container(method))
546         .WillOnce(DoAll(TraceDbus("sd_bus_message_close_container() -> 0"),
547                         Return(0)));
548 
549     EXPECT_CALL(mock, sd_bus_call(_,         // sd_bus *bus,
550                                   method,    // sd_bus_message *m
551                                   _,         // uint64_t timeout
552                                   NotNull(), // sd_bus_error *ret_error
553                                   IsNull())) // sd_bus_message **reply
554         .WillOnce(DoAll(TraceDbus("sd_bus_call() -> ret_val"),
555                         SetArgPointee<3>(SD_BUS_ERROR_NULL),
556                         Return(sd_bus_call_return_value)));
557 }
558 
559 TEST(HandlerTest, accelOobWrite_Success)
560 {
561     StrictMock<sdbusplus::SdBusMock> mock;
562     MockDbusHandler h(mock);
563 
564     constexpr uint64_t address = 0x123456789abcdef;
565     constexpr uint8_t num_bytes = sizeof(uint64_t);
566     constexpr int sd_bus_call_return_value = 1;
567     constexpr uint64_t data = 0x13579bdf02468ace;
568 
569     ExpectWrite(mock, address, num_bytes, data, sd_bus_call_return_value);
570     EXPECT_NO_THROW(h.accelOobWrite("test/path", address, num_bytes, data));
571 }
572 
573 TEST(HandlerTest, accelOobRead_TooManyBytesRequested)
574 {
575     StrictMock<sdbusplus::SdBusMock> mock;
576     MockDbusHandler h(mock);
577 
578     constexpr uint64_t address = 0x123456789abcdef;
579     constexpr uint8_t num_bytes = sizeof(uint64_t) + 1;
580     constexpr uint64_t data = 0x13579bdf02468ace;
581 
582     EXPECT_THROW(h.accelOobWrite("test/path", address, num_bytes, data),
583                  IpmiException);
584 }
585 
586 TEST(HandlerTest, accelOobWrite_Fail)
587 {
588     StrictMock<sdbusplus::SdBusMock> mock;
589     MockDbusHandler h(mock);
590 
591     constexpr uint64_t address = 0x123456789abcdef;
592     constexpr uint8_t num_bytes = sizeof(uint64_t);
593     constexpr int sd_bus_call_return_value = -ENOTCONN;
594     constexpr uint64_t data = 0x13579bdf02468ace;
595 
596     ExpectWrite(mock, address, num_bytes, data, sd_bus_call_return_value);
597     EXPECT_THROW(h.accelOobWrite("test/path", address, num_bytes, data),
598                  IpmiException);
599 }
600 
601 TEST(HandlerTest, PcieBifurcation)
602 {
603     const std::string& testJson = "/tmp/test-json";
604     auto j = R"(
605         {
606             "1": [ 1, 3 ],
607             "3": [ 3, 6 ],
608             "4": [ 3, 4, 1 ],
609             "6": [ 8 ]
610         }
611     )"_json;
612 
613     std::ofstream bifurcationJson(testJson);
614     bifurcationJson << j.dump();
615     bifurcationJson.flush();
616     bifurcationJson.close();
617 
618     BifurcationStatic bifurcationHelper(testJson);
619     Handler h(std::ref(bifurcationHelper));
620 
621     std::unordered_map<uint8_t, std::vector<uint8_t>> expectedMapping = {
622         {1, {1, 3}}, {3, {3, 6}}, {4, {3, 4, 1}}, {6, {8}}};
623     std::vector<uint8_t> invalidBus = {0, 2, 5, 7};
624 
625     for (const auto& [bus, output] : expectedMapping)
626     {
627         EXPECT_THAT(h.pcieBifurcation(bus), ContainerEq(output));
628     }
629 
630     for (const auto& bus : invalidBus)
631     {
632         EXPECT_TRUE(h.pcieBifurcation(bus).empty());
633     }
634 
635     std::filesystem::remove(testJson.data());
636     bifurcationHelper = BifurcationStatic(testJson);
637     Handler h2(std::ref(bifurcationHelper));
638     for (uint8_t i = 0; i < 8; ++i)
639     {
640         auto bifurcation = h2.pcieBifurcation(i);
641         EXPECT_TRUE(bifurcation.empty());
642     }
643 }
644 
645 // TODO: Add checks for other functions of handler.
646 
647 } // namespace ipmi
648 } // namespace google
649