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
TEST(HandlerTest,EthCheckValidHappy)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
TEST(HandlerTest,CableCheckIllegalPath)51 TEST(HandlerTest, CableCheckIllegalPath)
52 {
53 Handler h;
54 EXPECT_THROW(h.getRxPackets("eth0/../../"), IpmiException);
55 }
56
TEST(HandlerTest,readNameFromConfigInstanceVariety)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
TEST(HandlerTest,getEntityNameWithNameNotFoundExcepts)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
TEST(HandlerTest,getEntityNameWithNameFoundReturnsIt)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:
MockDbusHandler(sdbusplus::SdBusMock & mock,const std::string & config="")143 MockDbusHandler(sdbusplus::SdBusMock& mock,
144 const std::string& config = "") :
145 Handler(config), mock_(&mock)
146 {}
147
148 protected:
getDbus() const149 sdbusplus::bus_t 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
ACTION_TEMPLATE(AssignReadVal,HAS_1_TEMPLATE_PARAMS (typename,T),AND_1_VALUE_PARAMS (val))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
ACTION_P(TraceDbus,msg)165 ACTION_P(TraceDbus, msg)
166 {
167 stdplus::print(stderr, "{}\n", msg);
168 }
169
ACTION_P(TraceDbus2,msg)170 ACTION_P(TraceDbus2, msg)
171 {
172 stdplus::print(stderr, "{}({:02x})\n", msg,
173 *static_cast<const uint8_t*>(arg2));
174 }
175
176 constexpr char object_path[] = "/com/google/customAccel/test/path";
177 constexpr char property_grpc[] = "com.google.custom_accel.gRPC";
178 constexpr char value_port[] = "Port";
179 constexpr uint32_t port = 5000;
180
181 constexpr char SD_BUS_TYPE_BYTE_STR[] = {SD_BUS_TYPE_BYTE, '\0'};
182
183 // Returns an object that looks like:
184 // "/com/google/customAccel/test/path": {
185 // "com.google.custom_accel.gRPC" : {
186 // "Port" : {
187 // "type" : "u",
188 // "data" : 5000
189 // }
190 // }
191 // }
ExpectGetManagedObjects(StrictMock<sdbusplus::SdBusMock> & mock,const char * obj_path=object_path)192 void ExpectGetManagedObjects(StrictMock<sdbusplus::SdBusMock>& mock,
193 const char* obj_path = object_path)
194 {
195 ::testing::InSequence s;
196
197 // These must be nullptr or sd_bus_message_unref will seg fault.
198 constexpr sd_bus_message* method = nullptr;
199 constexpr sd_bus_message* msg = nullptr;
200
201 EXPECT_CALL(mock, sd_bus_message_new_method_call(
202 _, // sd_bus *bus,
203 NotNull(), // sd_bus_message **m
204 StrEq("com.google.custom_accel"), StrEq("/"),
205 StrEq("org.freedesktop.DBus.ObjectManager"),
206 StrEq("GetManagedObjects")))
207 .WillOnce(DoAll(SetArgPointee<1>(method), Return(0)));
208
209 EXPECT_CALL(mock, sd_bus_call(_, // sd_bus *bus,
210 method, // sd_bus_message *m
211 _, // uint64_t timeout
212 NotNull(), // sd_bus_error *ret_error
213 NotNull())) // sd_bus_message **reply
214 .WillOnce(DoAll(SetArgPointee<3>(SD_BUS_ERROR_NULL),
215 SetArgPointee<4>(msg), // reply
216 Return(0)));
217
218 EXPECT_CALL(mock, sd_bus_message_enter_container(msg, SD_BUS_TYPE_ARRAY,
219 StrEq("{oa{sa{sv}}}")))
220 .WillOnce(Return(1));
221
222 EXPECT_CALL(mock, sd_bus_message_at_end(msg, 0)).WillOnce(Return(0));
223
224 EXPECT_CALL(mock, sd_bus_message_enter_container(
225 msg, SD_BUS_TYPE_DICT_ENTRY, StrEq("oa{sa{sv}}")))
226 .WillOnce(Return(1));
227
228 EXPECT_CALL(mock, sd_bus_message_read_basic(msg, SD_BUS_TYPE_OBJECT_PATH,
229 NotNull()))
230 .WillOnce(DoAll(AssignReadVal<const char*>(obj_path), Return(1)));
231
232 EXPECT_CALL(mock, sd_bus_message_enter_container(msg, SD_BUS_TYPE_ARRAY,
233 StrEq("{sa{sv}}")))
234 .WillOnce(Return(1));
235
236 EXPECT_CALL(mock, sd_bus_message_at_end(msg, 0)).WillOnce(Return(0));
237
238 EXPECT_CALL(mock, sd_bus_message_enter_container(
239 msg, SD_BUS_TYPE_DICT_ENTRY, StrEq("sa{sv}")))
240 .WillOnce(Return(1));
241
242 EXPECT_CALL(mock,
243 sd_bus_message_read_basic(msg, SD_BUS_TYPE_STRING, NotNull()))
244 .WillOnce(DoAll(AssignReadVal<const char*>(property_grpc), Return(1)));
245
246 EXPECT_CALL(mock, sd_bus_message_enter_container(msg, SD_BUS_TYPE_ARRAY,
247 StrEq("{sv}")))
248 .WillOnce(Return(1));
249
250 EXPECT_CALL(mock, sd_bus_message_at_end(msg, 0)).WillOnce(Return(0));
251
252 EXPECT_CALL(mock, sd_bus_message_enter_container(
253 msg, SD_BUS_TYPE_DICT_ENTRY, StrEq("sv")))
254 .WillOnce(Return(1));
255
256 EXPECT_CALL(mock,
257 sd_bus_message_read_basic(msg, SD_BUS_TYPE_STRING, NotNull()))
258 .WillOnce(DoAll(AssignReadVal<const char*>(value_port), Return(1)));
259
260 EXPECT_CALL(
261 mock, sd_bus_message_verify_type(msg, SD_BUS_TYPE_VARIANT, StrNe("u")))
262 .Times(AnyNumber())
263 .WillRepeatedly(Return(0));
264
265 EXPECT_CALL(
266 mock, sd_bus_message_verify_type(msg, SD_BUS_TYPE_VARIANT, StrEq("u")))
267 .WillOnce(Return(1));
268
269 EXPECT_CALL(mock, sd_bus_message_enter_container(msg, SD_BUS_TYPE_VARIANT,
270 StrEq("u")))
271 .WillOnce(Return(1));
272
273 EXPECT_CALL(mock,
274 sd_bus_message_read_basic(msg, SD_BUS_TYPE_UINT32, NotNull()))
275 .WillOnce(DoAll(AssignReadVal<uint32_t>(port), Return(0)));
276
277 EXPECT_CALL(
278 mock, sd_bus_message_verify_type(msg, SD_BUS_TYPE_VARIANT, StrNe("u")))
279 .Times(AnyNumber())
280 .WillRepeatedly(Return(0));
281
282 EXPECT_CALL(mock, sd_bus_message_exit_container(msg))
283 .WillOnce(Return(1))
284 .WillOnce(Return(1));
285
286 EXPECT_CALL(mock, sd_bus_message_at_end(msg, 0)).WillOnce(Return(1));
287
288 EXPECT_CALL(mock, sd_bus_message_exit_container(msg))
289 .WillOnce(Return(1))
290 .WillOnce(Return(1));
291
292 EXPECT_CALL(mock, sd_bus_message_at_end(msg, 0)).WillOnce(Return(1));
293
294 EXPECT_CALL(mock, sd_bus_message_exit_container(msg))
295 .WillOnce(Return(1))
296 .WillOnce(Return(1));
297
298 EXPECT_CALL(mock, sd_bus_message_at_end(msg, 0)).WillOnce(Return(1));
299
300 EXPECT_CALL(mock, sd_bus_message_exit_container(msg)).WillOnce(Return(1));
301 }
302
ExpectSdBusError(StrictMock<sdbusplus::SdBusMock> & mock,const std::string & service,const std::string & objPath,const std::string & interface,const std::string & function)303 void ExpectSdBusError(StrictMock<sdbusplus::SdBusMock>& mock,
304 const std::string& service, const std::string& objPath,
305 const std::string& interface, const std::string& function)
306 {
307 EXPECT_CALL(mock, sd_bus_message_new_method_call(
308 _, // sd_bus *bus,
309 NotNull(), // sd_bus_message **m
310 StrEq(service), StrEq(objPath), StrEq(interface),
311 StrEq(function)))
312 .WillOnce(Return(-ENOTCONN));
313 }
314
TEST(HandlerTest,accelOobDeviceCount_Success)315 TEST(HandlerTest, accelOobDeviceCount_Success)
316 {
317 StrictMock<sdbusplus::SdBusMock> mock;
318 MockDbusHandler h(mock);
319 ExpectGetManagedObjects(mock);
320 EXPECT_EQ(1, h.accelOobDeviceCount());
321 }
322
TEST(HandlerTest,accelOobDeviceCount_Fail)323 TEST(HandlerTest, accelOobDeviceCount_Fail)
324 {
325 StrictMock<sdbusplus::SdBusMock> mock;
326 MockDbusHandler h(mock);
327 ExpectSdBusError(mock, "com.google.custom_accel", "/",
328 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
329 EXPECT_THROW(h.accelOobDeviceCount(), IpmiException);
330 }
331
TEST(HandlerTest,accelOobDeviceName_Success)332 TEST(HandlerTest, accelOobDeviceName_Success)
333 {
334 StrictMock<sdbusplus::SdBusMock> mock;
335 MockDbusHandler h(mock);
336 ExpectGetManagedObjects(mock);
337 EXPECT_EQ(std::string("test/path"), h.accelOobDeviceName(0));
338 }
339
TEST(HandlerTest,accelOobDeviceName_Fail)340 TEST(HandlerTest, accelOobDeviceName_Fail)
341 {
342 StrictMock<sdbusplus::SdBusMock> mock;
343 MockDbusHandler h(mock);
344 ExpectSdBusError(mock, "com.google.custom_accel", "/",
345 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
346 EXPECT_THROW(h.accelOobDeviceName(0), IpmiException);
347 }
348
TEST(HandlerTest,accelOobDeviceName_OutOfRange)349 TEST(HandlerTest, accelOobDeviceName_OutOfRange)
350 {
351 StrictMock<sdbusplus::SdBusMock> mock;
352 MockDbusHandler h(mock);
353 ExpectGetManagedObjects(mock);
354 EXPECT_THROW(h.accelOobDeviceName(1), IpmiException);
355 }
356
TEST(HandlerTest,accelOobDeviceName_InvalidName)357 TEST(HandlerTest, accelOobDeviceName_InvalidName)
358 {
359 constexpr char bad_object_path[] = "/com/google/customAccel2/bad/path";
360 StrictMock<sdbusplus::SdBusMock> mock;
361 MockDbusHandler h(mock);
362 ExpectGetManagedObjects(mock, bad_object_path);
363 EXPECT_THROW(h.accelOobDeviceName(0), IpmiException);
364 }
365
366 constexpr uint8_t NUM_BYTES_RETURNED_EQ_NUM_BYTES = 0xff;
ExpectRead(StrictMock<sdbusplus::SdBusMock> & mock,uint64_t address,uint8_t num_bytes,uint64_t data,int sd_bus_call_return_value,uint8_t num_bytes_returned=NUM_BYTES_RETURNED_EQ_NUM_BYTES)367 void ExpectRead(StrictMock<sdbusplus::SdBusMock>& mock, uint64_t address,
368 uint8_t num_bytes, uint64_t data, int sd_bus_call_return_value,
369 uint8_t num_bytes_returned = NUM_BYTES_RETURNED_EQ_NUM_BYTES)
370 {
371 ::testing::InSequence s;
372
373 // These must be nullptr or sd_bus_message_unref will seg fault.
374 constexpr sd_bus_message* method = nullptr;
375 constexpr sd_bus_message* msg = nullptr;
376
377 EXPECT_CALL(mock, sd_bus_message_new_method_call(
378 _, // sd_bus *bus,
379 NotNull(), // sd_bus_message **m
380 StrEq("com.google.custom_accel"),
381 StrEq("/com/google/customAccel/test/path"),
382 StrEq("com.google.custom_accel.BAR"), StrEq("Read")))
383 .WillOnce(DoAll(SetArgPointee<1>(method), Return(0)));
384
385 EXPECT_CALL(mock,
386 sd_bus_message_append_basic(
387 method, SD_BUS_TYPE_UINT64,
388 MatcherCast<const void*>(SafeMatcherCast<const uint64_t*>(
389 Pointee(Eq(address))))))
390 .WillOnce(Return(1));
391
392 EXPECT_CALL(mock,
393 sd_bus_message_append_basic(
394 method, SD_BUS_TYPE_UINT64,
395 MatcherCast<const void*>(SafeMatcherCast<const uint64_t*>(
396 Pointee(Eq<uint64_t>(num_bytes))))))
397 .WillOnce(Return(1));
398
399 EXPECT_CALL(mock, sd_bus_call(_, // sd_bus *bus,
400 method, // sd_bus_message *m
401 _, // uint64_t timeout
402 NotNull(), // sd_bus_error *ret_error
403 NotNull())) // sd_bus_message **reply
404 .WillOnce(DoAll(SetArgPointee<3>(SD_BUS_ERROR_NULL),
405 SetArgPointee<4>(msg), // reply
406 Return(sd_bus_call_return_value)));
407
408 if (sd_bus_call_return_value >= 0)
409 {
410 EXPECT_CALL(mock,
411 sd_bus_message_enter_container(msg, SD_BUS_TYPE_ARRAY,
412 StrEq(SD_BUS_TYPE_BYTE_STR)))
413 .WillOnce(Return(1));
414
415 if (num_bytes_returned == NUM_BYTES_RETURNED_EQ_NUM_BYTES)
416 {
417 num_bytes_returned = num_bytes;
418 }
419 for (auto i = num_bytes_returned - 1; i >= 0; --i)
420 {
421 EXPECT_CALL(mock, sd_bus_message_at_end(msg, 0))
422 .WillOnce(Return(0));
423
424 const uint8_t byte = (i >= 8) ? 0 : (data >> (8 * i)) & 0xff;
425 EXPECT_CALL(mock, sd_bus_message_read_basic(msg, SD_BUS_TYPE_BYTE,
426 NotNull()))
427 .WillOnce(DoAll(AssignReadVal<uint8_t>(byte), Return(1)));
428 }
429
430 EXPECT_CALL(mock, sd_bus_message_at_end(msg, 0)).WillOnce(Return(1));
431
432 EXPECT_CALL(mock, sd_bus_message_exit_container(msg))
433 .WillOnce(Return(1));
434 }
435 }
436
TEST(HandlerTest,accelOobRead_Success)437 TEST(HandlerTest, accelOobRead_Success)
438 {
439 StrictMock<sdbusplus::SdBusMock> mock;
440 MockDbusHandler h(mock);
441
442 constexpr uint64_t address = 0x123456789abcdef;
443 constexpr uint8_t num_bytes = sizeof(uint64_t);
444 constexpr int sd_bus_call_return_value = 1;
445 constexpr uint64_t data = 0x13579bdf02468ace;
446
447 ExpectRead(mock, address, num_bytes, data, sd_bus_call_return_value);
448 EXPECT_EQ(data, h.accelOobRead("test/path", address, num_bytes));
449 }
450
TEST(HandlerTest,accelOobRead_Fail)451 TEST(HandlerTest, accelOobRead_Fail)
452 {
453 StrictMock<sdbusplus::SdBusMock> mock;
454 MockDbusHandler h(mock);
455
456 constexpr uint64_t address = 0x123456789abcdef;
457 constexpr uint8_t num_bytes = sizeof(uint64_t);
458 constexpr int sd_bus_call_return_value = -ENOTCONN;
459 constexpr uint64_t data = 0x13579bdf02468ace;
460
461 ExpectRead(mock, address, num_bytes, data, sd_bus_call_return_value);
462 EXPECT_THROW(h.accelOobRead("test/path", address, num_bytes),
463 IpmiException);
464 }
465
TEST(HandlerTest,accelOobRead_TooFewBytesReturned)466 TEST(HandlerTest, accelOobRead_TooFewBytesReturned)
467 {
468 StrictMock<sdbusplus::SdBusMock> mock;
469 MockDbusHandler h(mock);
470
471 constexpr uint64_t address = 0x123456789abcdef;
472 constexpr uint8_t num_bytes = sizeof(uint64_t);
473 constexpr int sd_bus_call_return_value = 1;
474 constexpr uint64_t data = 0x13579bdf02468ace;
475 constexpr uint8_t num_bytes_returned = num_bytes - 1;
476
477 ExpectRead(mock, address, num_bytes, data, sd_bus_call_return_value,
478 num_bytes_returned);
479 EXPECT_THROW(h.accelOobRead("test/path", address, num_bytes),
480 IpmiException);
481 }
482
TEST(HandlerTest,accelOobRead_TooManyBytesReturned)483 TEST(HandlerTest, accelOobRead_TooManyBytesReturned)
484 {
485 StrictMock<sdbusplus::SdBusMock> mock;
486 MockDbusHandler h(mock);
487
488 constexpr uint64_t address = 0x123456789abcdef;
489 constexpr uint8_t num_bytes = sizeof(uint64_t);
490 constexpr int sd_bus_call_return_value = 1;
491 constexpr uint64_t data = 0x13579bdf02468ace;
492 constexpr uint8_t num_bytes_returned = sizeof(uint64_t) + 1;
493
494 ExpectRead(mock, address, num_bytes, data, sd_bus_call_return_value,
495 num_bytes_returned);
496 EXPECT_THROW(h.accelOobRead("test/path", address, num_bytes),
497 IpmiException);
498 }
499
ExpectWrite(StrictMock<sdbusplus::SdBusMock> & mock,uint64_t address,uint8_t num_bytes,uint64_t data,int sd_bus_call_return_value)500 void ExpectWrite(StrictMock<sdbusplus::SdBusMock>& mock, uint64_t address,
501 uint8_t num_bytes, uint64_t data, int sd_bus_call_return_value)
502 {
503 ::testing::InSequence s;
504
505 // These must be nullptr or sd_bus_message_unref will seg fault.
506 constexpr sd_bus_message* method = nullptr;
507
508 EXPECT_CALL(mock, sd_bus_message_new_method_call(
509 _, // sd_bus *bus,
510 NotNull(), // sd_bus_message **m
511 StrEq("com.google.custom_accel"),
512 StrEq("/com/google/customAccel/test/path"),
513 StrEq("com.google.custom_accel.BAR"), StrEq("Write")))
514 .WillOnce(DoAll(TraceDbus("sd_bus_message_new_method_call"),
515 SetArgPointee<1>(method), Return(0)));
516
517 EXPECT_CALL(mock,
518 sd_bus_message_append_basic(
519 method, SD_BUS_TYPE_UINT64,
520 MatcherCast<const void*>(SafeMatcherCast<const uint64_t*>(
521 Pointee(Eq(address))))))
522 .WillOnce(DoAll(TraceDbus("sd_bus_message_append_basic(address) -> 1"),
523 Return(1)));
524
525 EXPECT_CALL(mock,
526 sd_bus_message_open_container(method, SD_BUS_TYPE_ARRAY,
527 StrEq(SD_BUS_TYPE_BYTE_STR)))
528 .WillOnce(DoAll(TraceDbus("sd_bus_message_open_container(a, y) -> 0"),
529 Return(0)));
530
531 for (auto i = 0; i < num_bytes; ++i)
532 {
533 const uint8_t byte = (data >> (8 * i)) & 0xff;
534
535 EXPECT_CALL(
536 mock, sd_bus_message_append_basic(
537 method, SD_BUS_TYPE_BYTE,
538 MatcherCast<const void*>(
539 SafeMatcherCast<const uint8_t*>(Pointee(Eq(byte))))))
540 .WillOnce(
541 DoAll(TraceDbus2("sd_bus_message_append_basic"), Return(1)));
542 }
543
544 EXPECT_CALL(mock, sd_bus_message_close_container(method))
545 .WillOnce(DoAll(TraceDbus("sd_bus_message_close_container() -> 0"),
546 Return(0)));
547
548 EXPECT_CALL(mock, sd_bus_call(_, // sd_bus *bus,
549 method, // sd_bus_message *m
550 _, // uint64_t timeout
551 NotNull(), // sd_bus_error *ret_error
552 IsNull())) // sd_bus_message **reply
553 .WillOnce(DoAll(TraceDbus("sd_bus_call() -> ret_val"),
554 SetArgPointee<3>(SD_BUS_ERROR_NULL),
555 Return(sd_bus_call_return_value)));
556 }
557
TEST(HandlerTest,accelOobWrite_Success)558 TEST(HandlerTest, accelOobWrite_Success)
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);
565 constexpr int sd_bus_call_return_value = 1;
566 constexpr uint64_t data = 0x13579bdf02468ace;
567
568 ExpectWrite(mock, address, num_bytes, data, sd_bus_call_return_value);
569 EXPECT_NO_THROW(h.accelOobWrite("test/path", address, num_bytes, data));
570 }
571
TEST(HandlerTest,accelOobRead_TooManyBytesRequested)572 TEST(HandlerTest, accelOobRead_TooManyBytesRequested)
573 {
574 StrictMock<sdbusplus::SdBusMock> mock;
575 MockDbusHandler h(mock);
576
577 constexpr uint64_t address = 0x123456789abcdef;
578 constexpr uint8_t num_bytes = sizeof(uint64_t) + 1;
579 constexpr uint64_t data = 0x13579bdf02468ace;
580
581 EXPECT_THROW(h.accelOobWrite("test/path", address, num_bytes, data),
582 IpmiException);
583 }
584
TEST(HandlerTest,accelOobWrite_Fail)585 TEST(HandlerTest, accelOobWrite_Fail)
586 {
587 StrictMock<sdbusplus::SdBusMock> mock;
588 MockDbusHandler h(mock);
589
590 constexpr uint64_t address = 0x123456789abcdef;
591 constexpr uint8_t num_bytes = sizeof(uint64_t);
592 constexpr int sd_bus_call_return_value = -ENOTCONN;
593 constexpr uint64_t data = 0x13579bdf02468ace;
594
595 ExpectWrite(mock, address, num_bytes, data, sd_bus_call_return_value);
596 EXPECT_THROW(h.accelOobWrite("test/path", address, num_bytes, data),
597 IpmiException);
598 }
599
TEST(HandlerTest,PcieBifurcation)600 TEST(HandlerTest, PcieBifurcation)
601 {
602 const std::string& testJson = "/tmp/test-json";
603 auto j = R"(
604 {
605 "1": [ 1, 3 ],
606 "3": [ 3, 6 ],
607 "4": [ 3, 4, 1 ],
608 "6": [ 8 ]
609 }
610 )"_json;
611
612 std::ofstream bifurcationJson(testJson);
613 bifurcationJson << j.dump();
614 bifurcationJson.flush();
615 bifurcationJson.close();
616
617 BifurcationStatic bifurcationHelper(testJson);
618 Handler h(std::ref(bifurcationHelper));
619
620 std::unordered_map<uint8_t, std::vector<uint8_t>> expectedMapping = {
621 {1, {1, 3}}, {3, {3, 6}}, {4, {3, 4, 1}}, {6, {8}}};
622 std::vector<uint8_t> invalidBus = {0, 2, 5, 7};
623
624 for (const auto& [bus, output] : expectedMapping)
625 {
626 EXPECT_THAT(h.pcieBifurcation(bus), ContainerEq(output));
627 }
628
629 for (const auto& bus : invalidBus)
630 {
631 EXPECT_TRUE(h.pcieBifurcation(bus).empty());
632 }
633
634 std::filesystem::remove(testJson.data());
635 bifurcationHelper = BifurcationStatic(testJson);
636 Handler h2(std::ref(bifurcationHelper));
637 for (uint8_t i = 0; i < 8; ++i)
638 {
639 auto bifurcation = h2.pcieBifurcation(i);
640 EXPECT_TRUE(bifurcation.empty());
641 }
642 }
643
TEST(HandlerTest,BmInstanceFailCase)644 TEST(HandlerTest, BmInstanceFailCase)
645 {
646 StrictMock<sdbusplus::SdBusMock> mock;
647 MockDbusHandler h(mock);
648
649 // Invalid enum
650 EXPECT_THROW(h.getBMInstanceProperty(0x07), IpmiException);
651
652 // Valid enum but no path exists
653 EXPECT_THROW(h.getBMInstanceProperty(0x00), IpmiException);
654 }
655
656 // TODO: Add checks for other functions of handler.
657
658 } // namespace ipmi
659 } // namespace google
660