xref: /openbmc/google-ipmi-sys/test/handler_unittest.cpp (revision 4134c74bda1bef375e155e193bad98741912c252)
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