1 #include "conf.hpp"
2 #include "dbus/dbuspassive.hpp"
3 #include "test/dbushelper_mock.hpp"
4
5 #include <sdbusplus/test/sdbus_mock.hpp>
6
7 #include <functional>
8 #include <memory>
9 #include <string>
10 #include <variant>
11
12 #include <gmock/gmock.h>
13 #include <gtest/gtest.h>
14
15 namespace pid_control
16 {
17 namespace
18 {
19
20 using ::testing::_;
21 using ::testing::InSequence;
22 using ::testing::Invoke;
23 using ::testing::IsNull;
24 using ::testing::NotNull;
25 using ::testing::Return;
26 using ::testing::StrEq;
27
28 std::string SensorIntf = "xyz.openbmc_project.Sensor.Value";
29
TEST(DbusPassiveTest,FactoryFailsWithInvalidType)30 TEST(DbusPassiveTest, FactoryFailsWithInvalidType)
31 {
32 // Verify the type is checked by the factory.
33
34 sdbusplus::SdBusMock sdbus_mock;
35 auto bus_mock = sdbusplus::get_mocked_new(&sdbus_mock);
36 std::string type = "invalid";
37 std::string id = "id";
38
39 auto helper = std::make_unique<DbusHelperMock>();
40 auto info = conf::SensorConfig();
41
42 std::unique_ptr<ReadInterface> ri = DbusPassive::createDbusPassive(
43 bus_mock, type, id, std::move(helper), &info, nullptr);
44
45 EXPECT_EQ(ri, nullptr);
46 }
47
TEST(DbusPassiveTest,BoringConstructorTest)48 TEST(DbusPassiveTest, BoringConstructorTest)
49 {
50 // Simply build the object, does no error checking.
51
52 sdbusplus::SdBusMock sdbus_mock;
53 auto bus_mock = sdbusplus::get_mocked_new(&sdbus_mock);
54 std::string type = "invalid";
55 std::string id = "id";
56 std::string path = "/xyz/openbmc_project/sensors/unknown/id";
57
58 auto helper = std::make_unique<DbusHelperMock>();
59 SensorProperties properties;
60
61 DbusPassive(bus_mock, type, id, std::move(helper), properties, false, path,
62 nullptr);
63 // Success
64 }
65
66 class DbusPassiveTestObj : public ::testing::Test
67 {
68 protected:
DbusPassiveTestObj()69 DbusPassiveTestObj() :
70 sdbus_mock(),
71 bus_mock(std::move(sdbusplus::get_mocked_new(&sdbus_mock))),
72 helper(std::make_unique<DbusHelperMock>())
73 {
74 EXPECT_CALL(*helper, getService(StrEq(SensorIntf), StrEq(path)))
75 .WillOnce(Return("asdf"));
76
77 EXPECT_CALL(*helper,
78 getProperties(StrEq("asdf"), StrEq(path), NotNull()))
79 .WillOnce(Invoke([&]([[maybe_unused]] const std::string& service,
80 [[maybe_unused]] const std::string& path,
81 SensorProperties* prop) {
82 prop->scale = _scale;
83 prop->value = _value;
84 prop->unit = "x";
85 prop->min = 0;
86 prop->max = 0;
87 prop->available = true;
88 }));
89 EXPECT_CALL(*helper, thresholdsAsserted(StrEq("asdf"), StrEq(path)))
90 .WillOnce(Return(false));
91
92 auto info = conf::SensorConfig();
93 info.unavailableAsFailed = true;
94 ri = DbusPassive::createDbusPassive(bus_mock, type, id,
95 std::move(helper), &info, nullptr);
96 passive = reinterpret_cast<DbusPassive*>(ri.get());
97 EXPECT_FALSE(passive == nullptr);
98 }
99
100 sdbusplus::SdBusMock sdbus_mock;
101 sdbusplus::bus_t bus_mock;
102 std::unique_ptr<DbusHelperMock> helper;
103 std::string type = "temp";
104 std::string id = "id";
105 std::string path = "/xyz/openbmc_project/sensors/temperature/id";
106 int64_t _scale = -3;
107 int64_t _value = 10;
108
109 std::unique_ptr<ReadInterface> ri;
110 DbusPassive* passive;
111 };
112
TEST_F(DbusPassiveTestObj,ReadReturnsExpectedValues)113 TEST_F(DbusPassiveTestObj, ReadReturnsExpectedValues)
114 {
115 // Verify read is returning the values.
116 ReadReturn v;
117 v.value = 0.01;
118 // TODO: updated is set when the value is created, so we can range check
119 // it.
120 ReadReturn r = passive->read();
121 EXPECT_EQ(v.value, r.value);
122 }
123
TEST_F(DbusPassiveTestObj,SetValueUpdatesValue)124 TEST_F(DbusPassiveTestObj, SetValueUpdatesValue)
125 {
126 // Verify setvalue does as advertised.
127
128 double value = 0.01;
129 passive->setValue(value);
130
131 // TODO: updated is set when the value is set, so we can range check it.
132 ReadReturn r = passive->read();
133 EXPECT_EQ(value, r.value);
134 }
135
TEST_F(DbusPassiveTestObj,GetScaleReturnsExpectedValue)136 TEST_F(DbusPassiveTestObj, GetScaleReturnsExpectedValue)
137 {
138 // Verify the scale is returned as expected.
139 EXPECT_EQ(_scale, passive->getScale());
140 }
141
TEST_F(DbusPassiveTestObj,getIDReturnsExpectedValue)142 TEST_F(DbusPassiveTestObj, getIDReturnsExpectedValue)
143 {
144 // Verify getID returns the expected value.
145 EXPECT_EQ(id, passive->getID());
146 }
147
TEST_F(DbusPassiveTestObj,GetMinValueReturnsExpectedValue)148 TEST_F(DbusPassiveTestObj, GetMinValueReturnsExpectedValue)
149 {
150 EXPECT_DOUBLE_EQ(0, passive->getMin());
151 }
152
TEST_F(DbusPassiveTestObj,VerifyHandlesDbusSignal)153 TEST_F(DbusPassiveTestObj, VerifyHandlesDbusSignal)
154 {
155 // The dbus passive sensor listens for updates and if it's the Value
156 // property, it needs to handle it.
157
158 EXPECT_CALL(sdbus_mock, sd_bus_message_ref(IsNull()))
159 .WillOnce(Return(nullptr));
160 sdbusplus::message_t msg(nullptr, &sdbus_mock);
161
162 const char* Value = "Value";
163 int64_t xValue = 10000;
164 const char* intf = "xyz.openbmc_project.Sensor.Value";
165 // string, std::map<std::string, std::variant<int64_t>>
166 // msg.read(msgSensor, msgData);
167
168 EXPECT_CALL(sdbus_mock, sd_bus_message_read_basic(IsNull(), 's', NotNull()))
169 .WillOnce(Invoke([&]([[maybe_unused]] sd_bus_message* m,
170 [[maybe_unused]] char type, void* p) {
171 const char** s = static_cast<const char**>(p);
172 // Read the first parameter, the string.
173 *s = intf;
174 return 0;
175 }))
176 .WillOnce(Invoke([&]([[maybe_unused]] sd_bus_message* m,
177 [[maybe_unused]] char type, void* p) {
178 const char** s = static_cast<const char**>(p);
179 *s = Value;
180 // Read the string in the pair (dictionary).
181 return 0;
182 }));
183
184 // std::map
185 EXPECT_CALL(sdbus_mock,
186 sd_bus_message_enter_container(IsNull(), 'a', StrEq("{sv}")))
187 .WillOnce(Return(0));
188
189 // while !at_end()
190 EXPECT_CALL(sdbus_mock, sd_bus_message_at_end(IsNull(), 0))
191 .WillOnce(Return(0))
192 .WillOnce(Return(1)); // So it exits the loop after reading one pair.
193
194 // std::pair
195 EXPECT_CALL(sdbus_mock,
196 sd_bus_message_enter_container(IsNull(), 'e', StrEq("sv")))
197 .WillOnce(Return(0));
198
199 EXPECT_CALL(sdbus_mock,
200 sd_bus_message_verify_type(IsNull(), 'v', StrEq("x")))
201 .WillOnce(Return(1));
202 EXPECT_CALL(sdbus_mock,
203 sd_bus_message_enter_container(IsNull(), 'v', StrEq("x")))
204 .WillOnce(Return(0));
205
206 EXPECT_CALL(sdbus_mock, sd_bus_message_read_basic(IsNull(), 'x', NotNull()))
207 .WillOnce(Invoke([&]([[maybe_unused]] sd_bus_message* m,
208 [[maybe_unused]] char type, void* p) {
209 int64_t* s = static_cast<int64_t*>(p);
210 *s = xValue;
211 return 0;
212 }));
213
214 EXPECT_CALL(sdbus_mock, sd_bus_message_exit_container(IsNull()))
215 .WillOnce(Return(0)) /* variant. */
216 .WillOnce(Return(0)) /* std::pair */
217 .WillOnce(Return(0)); /* std::map */
218
219 int rv = handleSensorValue(msg, passive);
220 EXPECT_EQ(rv, 0); // It's always 0.
221
222 ReadReturn r = passive->read();
223 EXPECT_EQ(10, r.value);
224 }
225
TEST_F(DbusPassiveTestObj,VerifyIgnoresOtherPropertySignal)226 TEST_F(DbusPassiveTestObj, VerifyIgnoresOtherPropertySignal)
227 {
228 // The dbus passive sensor listens for updates and if it's the Value
229 // property, it needs to handle it. In this case, it won't be.
230
231 EXPECT_CALL(sdbus_mock, sd_bus_message_ref(IsNull()))
232 .WillOnce(Return(nullptr));
233 sdbusplus::message_t msg(nullptr, &sdbus_mock);
234
235 const char* Scale = "Scale";
236 int64_t xScale = -6;
237 const char* intf = "xyz.openbmc_project.Sensor.Value";
238 // string, std::map<std::string, std::variant<int64_t>>
239 // msg.read(msgSensor, msgData);
240
241 EXPECT_CALL(sdbus_mock, sd_bus_message_read_basic(IsNull(), 's', NotNull()))
242 .WillOnce(Invoke([&]([[maybe_unused]] sd_bus_message* m,
243 [[maybe_unused]] char type, void* p) {
244 const char** s = static_cast<const char**>(p);
245 // Read the first parameter, the string.
246 *s = intf;
247 return 0;
248 }))
249 .WillOnce(Invoke([&]([[maybe_unused]] sd_bus_message* m,
250 [[maybe_unused]] char type, void* p) {
251 const char** s = static_cast<const char**>(p);
252 *s = Scale;
253 // Read the string in the pair (dictionary).
254 return 0;
255 }));
256
257 // std::map
258 EXPECT_CALL(sdbus_mock,
259 sd_bus_message_enter_container(IsNull(), 'a', StrEq("{sv}")))
260 .WillOnce(Return(0));
261
262 // while !at_end()
263 EXPECT_CALL(sdbus_mock, sd_bus_message_at_end(IsNull(), 0))
264 .WillOnce(Return(0))
265 .WillOnce(Return(1)); // So it exits the loop after reading one pair.
266
267 // std::pair
268 EXPECT_CALL(sdbus_mock,
269 sd_bus_message_enter_container(IsNull(), 'e', StrEq("sv")))
270 .WillOnce(Return(0));
271
272 EXPECT_CALL(sdbus_mock,
273 sd_bus_message_verify_type(IsNull(), 'v', StrEq("x")))
274 .WillOnce(Return(1));
275 EXPECT_CALL(sdbus_mock,
276 sd_bus_message_enter_container(IsNull(), 'v', StrEq("x")))
277 .WillOnce(Return(0));
278
279 EXPECT_CALL(sdbus_mock, sd_bus_message_read_basic(IsNull(), 'x', NotNull()))
280 .WillOnce(Invoke([&]([[maybe_unused]] sd_bus_message* m,
281 [[maybe_unused]] char type, void* p) {
282 int64_t* s = static_cast<int64_t*>(p);
283 *s = xScale;
284 return 0;
285 }));
286
287 EXPECT_CALL(sdbus_mock, sd_bus_message_exit_container(IsNull()))
288 .WillOnce(Return(0)) /* variant. */
289 .WillOnce(Return(0)) /* std::pair */
290 .WillOnce(Return(0)); /* std::map */
291
292 int rv = handleSensorValue(msg, passive);
293 EXPECT_EQ(rv, 0); // It's always 0.
294
295 ReadReturn r = passive->read();
296 EXPECT_EQ(0.01, r.value);
297 }
298
TEST_F(DbusPassiveTestObj,VerifyCriticalThresholdAssert)299 TEST_F(DbusPassiveTestObj, VerifyCriticalThresholdAssert)
300 {
301 // Verifies when a threshold is crossed the sensor goes into error state
302 EXPECT_CALL(sdbus_mock, sd_bus_message_ref(IsNull()))
303 .WillOnce(Return(nullptr));
304 sdbusplus::message_t msg(nullptr, &sdbus_mock);
305
306 const char* criticalAlarm = "CriticalAlarmHigh";
307 bool alarm = true;
308 const char* intf = "xyz.openbmc_project.Sensor.Threshold.Critical";
309
310 passive->setFailed(false);
311
312 EXPECT_CALL(sdbus_mock, sd_bus_message_read_basic(IsNull(), 's', NotNull()))
313 .WillOnce(Invoke([&]([[maybe_unused]] sd_bus_message* m,
314 [[maybe_unused]] char type, void* p) {
315 const char** s = static_cast<const char**>(p);
316 // Read the first parameter, the string.
317 *s = intf;
318 return 0;
319 }))
320 .WillOnce(Invoke([&]([[maybe_unused]] sd_bus_message* m,
321 [[maybe_unused]] char type, void* p) {
322 const char** s = static_cast<const char**>(p);
323 *s = criticalAlarm;
324 // Read the string in the pair (dictionary).
325 return 0;
326 }));
327
328 // std::map
329 EXPECT_CALL(sdbus_mock,
330 sd_bus_message_enter_container(IsNull(), 'a', StrEq("{sv}")))
331 .WillOnce(Return(0));
332
333 // while !at_end()
334 EXPECT_CALL(sdbus_mock, sd_bus_message_at_end(IsNull(), 0))
335 .WillOnce(Return(0))
336 .WillOnce(Return(1)); // So it exits the loop after reading one pair.
337
338 // std::pair
339 EXPECT_CALL(sdbus_mock,
340 sd_bus_message_enter_container(IsNull(), 'e', StrEq("sv")))
341 .WillOnce(Return(0));
342
343 EXPECT_CALL(sdbus_mock,
344 sd_bus_message_verify_type(IsNull(), 'v', StrEq("x")))
345 .WillOnce(Return(0));
346 EXPECT_CALL(sdbus_mock,
347 sd_bus_message_verify_type(IsNull(), 'v', StrEq("d")))
348 .WillOnce(Return(0));
349 EXPECT_CALL(sdbus_mock,
350 sd_bus_message_verify_type(IsNull(), 'v', StrEq("b")))
351 .WillOnce(Return(1));
352 EXPECT_CALL(sdbus_mock,
353 sd_bus_message_enter_container(IsNull(), 'v', StrEq("b")))
354 .WillOnce(Return(0));
355
356 EXPECT_CALL(sdbus_mock, sd_bus_message_read_basic(IsNull(), 'b', NotNull()))
357 .WillOnce(Invoke([&]([[maybe_unused]] sd_bus_message* m,
358 [[maybe_unused]] char type, void* p) {
359 bool* s = static_cast<bool*>(p);
360 *s = alarm;
361 return 0;
362 }));
363
364 EXPECT_CALL(sdbus_mock, sd_bus_message_exit_container(IsNull()))
365 .WillOnce(Return(0)) /* variant. */
366 .WillOnce(Return(0)) /* std::pair */
367 .WillOnce(Return(0)); /* std::map */
368
369 int rv = handleSensorValue(msg, passive);
370 EXPECT_EQ(rv, 0); // It's always 0.
371 bool failed = passive->getFailed();
372 EXPECT_EQ(failed, true);
373 }
374
TEST_F(DbusPassiveTestObj,VerifyCriticalThresholdDeassert)375 TEST_F(DbusPassiveTestObj, VerifyCriticalThresholdDeassert)
376 {
377 // Verifies when a threshold is deasserted a failed sensor goes back into
378 // the normal state
379 EXPECT_CALL(sdbus_mock, sd_bus_message_ref(IsNull()))
380 .WillOnce(Return(nullptr));
381 sdbusplus::message_t msg(nullptr, &sdbus_mock);
382
383 const char* criticalAlarm = "CriticalAlarmHigh";
384 bool alarm = false;
385 const char* intf = "xyz.openbmc_project.Sensor.Threshold.Critical";
386
387 passive->setFailed(true);
388
389 EXPECT_CALL(sdbus_mock, sd_bus_message_read_basic(IsNull(), 's', NotNull()))
390 .WillOnce(Invoke([&]([[maybe_unused]] sd_bus_message* m,
391 [[maybe_unused]] char type, void* p) {
392 const char** s = static_cast<const char**>(p);
393 // Read the first parameter, the string.
394 *s = intf;
395 return 0;
396 }))
397 .WillOnce(Invoke([&]([[maybe_unused]] sd_bus_message* m,
398 [[maybe_unused]] char type, void* p) {
399 const char** s = static_cast<const char**>(p);
400 *s = criticalAlarm;
401 // Read the string in the pair (dictionary).
402 return 0;
403 }));
404
405 // std::map
406 EXPECT_CALL(sdbus_mock,
407 sd_bus_message_enter_container(IsNull(), 'a', StrEq("{sv}")))
408 .WillOnce(Return(0));
409
410 // while !at_end()
411 EXPECT_CALL(sdbus_mock, sd_bus_message_at_end(IsNull(), 0))
412 .WillOnce(Return(0))
413 .WillOnce(Return(1)); // So it exits the loop after reading one pair.
414
415 // std::pair
416 EXPECT_CALL(sdbus_mock,
417 sd_bus_message_enter_container(IsNull(), 'e', StrEq("sv")))
418 .WillOnce(Return(0));
419
420 EXPECT_CALL(sdbus_mock,
421 sd_bus_message_verify_type(IsNull(), 'v', StrEq("x")))
422 .WillOnce(Return(0));
423 EXPECT_CALL(sdbus_mock,
424 sd_bus_message_verify_type(IsNull(), 'v', StrEq("d")))
425 .WillOnce(Return(0));
426 EXPECT_CALL(sdbus_mock,
427 sd_bus_message_verify_type(IsNull(), 'v', StrEq("b")))
428 .WillOnce(Return(1));
429 EXPECT_CALL(sdbus_mock,
430 sd_bus_message_enter_container(IsNull(), 'v', StrEq("b")))
431 .WillOnce(Return(0));
432
433 EXPECT_CALL(sdbus_mock, sd_bus_message_read_basic(IsNull(), 'b', NotNull()))
434 .WillOnce(Invoke([&]([[maybe_unused]] sd_bus_message* m,
435 [[maybe_unused]] char type, void* p) {
436 bool* s = static_cast<bool*>(p);
437 *s = alarm;
438 return 0;
439 }));
440
441 EXPECT_CALL(sdbus_mock, sd_bus_message_exit_container(IsNull()))
442 .WillOnce(Return(0)) /* variant. */
443 .WillOnce(Return(0)) /* std::pair */
444 .WillOnce(Return(0)); /* std::map */
445
446 int rv = handleSensorValue(msg, passive);
447 EXPECT_EQ(rv, 0); // It's always 0.
448 bool failed = passive->getFailed();
449 EXPECT_EQ(failed, false);
450 }
451
TEST_F(DbusPassiveTestObj,VerifyAvailableDeassert)452 TEST_F(DbusPassiveTestObj, VerifyAvailableDeassert)
453 {
454 // Verifies when Availble is deasserted && unavailableAsFailed == true,
455 // the sensor goes into error state
456 EXPECT_CALL(sdbus_mock, sd_bus_message_ref(IsNull()))
457 .WillOnce(Return(nullptr));
458 sdbusplus::message_t msg(nullptr, &sdbus_mock);
459
460 const char* property = "Available";
461 bool asserted = false;
462 const char* intf = "xyz.openbmc_project.State.Decorator.Availability";
463
464 passive->setAvailable(true);
465
466 EXPECT_CALL(sdbus_mock, sd_bus_message_read_basic(IsNull(), 's', NotNull()))
467 .WillOnce(Invoke([&]([[maybe_unused]] sd_bus_message* m,
468 [[maybe_unused]] char type, void* p) {
469 const char** s = static_cast<const char**>(p);
470 // Read the first parameter, the string.
471 *s = intf;
472 return 0;
473 }))
474 .WillOnce(Invoke([&]([[maybe_unused]] sd_bus_message* m,
475 [[maybe_unused]] char type, void* p) {
476 const char** s = static_cast<const char**>(p);
477 *s = property;
478 // Read the string in the pair (dictionary).
479 return 0;
480 }));
481
482 // std::map
483 EXPECT_CALL(sdbus_mock,
484 sd_bus_message_enter_container(IsNull(), 'a', StrEq("{sv}")))
485 .WillOnce(Return(0));
486
487 // while !at_end()
488 EXPECT_CALL(sdbus_mock, sd_bus_message_at_end(IsNull(), 0))
489 .WillOnce(Return(0))
490 .WillOnce(Return(1)); // So it exits the loop after reading one pair.
491
492 // std::pair
493 EXPECT_CALL(sdbus_mock,
494 sd_bus_message_enter_container(IsNull(), 'e', StrEq("sv")))
495 .WillOnce(Return(0));
496
497 EXPECT_CALL(sdbus_mock,
498 sd_bus_message_verify_type(IsNull(), 'v', StrEq("x")))
499 .WillOnce(Return(0));
500 EXPECT_CALL(sdbus_mock,
501 sd_bus_message_verify_type(IsNull(), 'v', StrEq("d")))
502 .WillOnce(Return(0));
503 EXPECT_CALL(sdbus_mock,
504 sd_bus_message_verify_type(IsNull(), 'v', StrEq("b")))
505 .WillOnce(Return(1));
506 EXPECT_CALL(sdbus_mock,
507 sd_bus_message_enter_container(IsNull(), 'v', StrEq("b")))
508 .WillOnce(Return(0));
509
510 EXPECT_CALL(sdbus_mock, sd_bus_message_read_basic(IsNull(), 'b', NotNull()))
511 .WillOnce(Invoke([&]([[maybe_unused]] sd_bus_message* m,
512 [[maybe_unused]] char type, void* p) {
513 bool* s = static_cast<bool*>(p);
514 *s = asserted;
515 return 0;
516 }));
517
518 EXPECT_CALL(sdbus_mock, sd_bus_message_exit_container(IsNull()))
519 .WillOnce(Return(0)) /* variant. */
520 .WillOnce(Return(0)) /* std::pair */
521 .WillOnce(Return(0)); /* std::map */
522
523 int rv = handleSensorValue(msg, passive);
524 EXPECT_EQ(rv, 0); // It's always 0.
525 bool failed = passive->getFailed();
526 EXPECT_EQ(failed, true);
527 }
528
TEST_F(DbusPassiveTestObj,VerifyAvailableAssert)529 TEST_F(DbusPassiveTestObj, VerifyAvailableAssert)
530 {
531 // Verifies when Availble is asserted && unavailableAsFailed == true,
532 // an error sensor goes back to normal state
533 EXPECT_CALL(sdbus_mock, sd_bus_message_ref(IsNull()))
534 .WillOnce(Return(nullptr));
535 sdbusplus::message_t msg(nullptr, &sdbus_mock);
536
537 const char* property = "Available";
538 bool asserted = true;
539 const char* intf = "xyz.openbmc_project.State.Decorator.Availability";
540
541 passive->setAvailable(false);
542 bool failed = passive->getFailed();
543 EXPECT_EQ(failed, true);
544
545 EXPECT_CALL(sdbus_mock, sd_bus_message_read_basic(IsNull(), 's', NotNull()))
546 .WillOnce(Invoke([&]([[maybe_unused]] sd_bus_message* m,
547 [[maybe_unused]] char type, void* p) {
548 const char** s = static_cast<const char**>(p);
549 // Read the first parameter, the string.
550 *s = intf;
551 return 0;
552 }))
553 .WillOnce(Invoke([&]([[maybe_unused]] sd_bus_message* m,
554 [[maybe_unused]] char type, void* p) {
555 const char** s = static_cast<const char**>(p);
556 *s = property;
557 // Read the string in the pair (dictionary).
558 return 0;
559 }));
560
561 // std::map
562 EXPECT_CALL(sdbus_mock,
563 sd_bus_message_enter_container(IsNull(), 'a', StrEq("{sv}")))
564 .WillOnce(Return(0));
565
566 // while !at_end()
567 EXPECT_CALL(sdbus_mock, sd_bus_message_at_end(IsNull(), 0))
568 .WillOnce(Return(0))
569 .WillOnce(Return(1)); // So it exits the loop after reading one pair.
570
571 // std::pair
572 EXPECT_CALL(sdbus_mock,
573 sd_bus_message_enter_container(IsNull(), 'e', StrEq("sv")))
574 .WillOnce(Return(0));
575
576 EXPECT_CALL(sdbus_mock,
577 sd_bus_message_verify_type(IsNull(), 'v', StrEq("x")))
578 .WillOnce(Return(0));
579 EXPECT_CALL(sdbus_mock,
580 sd_bus_message_verify_type(IsNull(), 'v', StrEq("d")))
581 .WillOnce(Return(0));
582 EXPECT_CALL(sdbus_mock,
583 sd_bus_message_verify_type(IsNull(), 'v', StrEq("b")))
584 .WillOnce(Return(1));
585 EXPECT_CALL(sdbus_mock,
586 sd_bus_message_enter_container(IsNull(), 'v', StrEq("b")))
587 .WillOnce(Return(0));
588
589 EXPECT_CALL(sdbus_mock, sd_bus_message_read_basic(IsNull(), 'b', NotNull()))
590 .WillOnce(Invoke([&]([[maybe_unused]] sd_bus_message* m,
591 [[maybe_unused]] char type, void* p) {
592 bool* s = static_cast<bool*>(p);
593 *s = asserted;
594 return 0;
595 }));
596
597 EXPECT_CALL(sdbus_mock, sd_bus_message_exit_container(IsNull()))
598 .WillOnce(Return(0)) /* variant. */
599 .WillOnce(Return(0)) /* std::pair */
600 .WillOnce(Return(0)); /* std::map */
601
602 int rv = handleSensorValue(msg, passive);
603 EXPECT_EQ(rv, 0); // It's always 0.
604 failed = passive->getFailed();
605 EXPECT_EQ(failed, false);
606 }
607
608 class DbusPassiveTestUnaSensorNotAsFailedObj : public ::testing::Test
609 {
610 protected:
DbusPassiveTestUnaSensorNotAsFailedObj()611 DbusPassiveTestUnaSensorNotAsFailedObj() :
612 sdbus_mock(),
613 bus_mock(std::move(sdbusplus::get_mocked_new(&sdbus_mock))),
614 helper(std::make_unique<DbusHelperMock>())
615 {
616 EXPECT_CALL(*helper, getService(StrEq(SensorIntf), StrEq(path)))
617 .WillOnce(Return("asdf"));
618
619 EXPECT_CALL(*helper,
620 getProperties(StrEq("asdf"), StrEq(path), NotNull()))
621 .WillOnce(Invoke([&]([[maybe_unused]] const std::string& service,
622 [[maybe_unused]] const std::string& path,
623 SensorProperties* prop) {
624 prop->scale = _scale;
625 prop->value = _value;
626 prop->unit = "x";
627 prop->min = 0;
628 prop->max = 0;
629 prop->available = true;
630 }));
631 EXPECT_CALL(*helper, thresholdsAsserted(StrEq("asdf"), StrEq(path)))
632 .WillOnce(Return(false));
633
634 auto info = conf::SensorConfig();
635 info.unavailableAsFailed = false;
636 ri = DbusPassive::createDbusPassive(bus_mock, type, id,
637 std::move(helper), &info, nullptr);
638 passive = reinterpret_cast<DbusPassive*>(ri.get());
639 EXPECT_FALSE(passive == nullptr);
640 }
641
642 sdbusplus::SdBusMock sdbus_mock;
643 sdbusplus::bus_t bus_mock;
644 std::unique_ptr<DbusHelperMock> helper;
645 std::string type = "temp";
646 std::string id = "id";
647 std::string path = "/xyz/openbmc_project/sensors/temperature/id";
648 int64_t _scale = -3;
649 int64_t _value = 10;
650
651 std::unique_ptr<ReadInterface> ri;
652 DbusPassive* passive;
653 };
654
TEST_F(DbusPassiveTestUnaSensorNotAsFailedObj,VerifyAvailableDeassert)655 TEST_F(DbusPassiveTestUnaSensorNotAsFailedObj, VerifyAvailableDeassert)
656 {
657 // Verifies when Availble is deasserted && unavailableAsFailed == false,
658 // the sensor remains at OK state but reading goes to NaN.
659 EXPECT_CALL(sdbus_mock, sd_bus_message_ref(IsNull()))
660 .WillOnce(Return(nullptr));
661 sdbusplus::message_t msg(nullptr, &sdbus_mock);
662
663 const char* property = "Available";
664 bool asserted = false;
665 const char* intf = "xyz.openbmc_project.State.Decorator.Availability";
666
667 passive->setAvailable(true);
668
669 EXPECT_CALL(sdbus_mock, sd_bus_message_read_basic(IsNull(), 's', NotNull()))
670 .WillOnce(Invoke([&]([[maybe_unused]] sd_bus_message* m,
671 [[maybe_unused]] char type, void* p) {
672 const char** s = static_cast<const char**>(p);
673 // Read the first parameter, the string.
674 *s = intf;
675 return 0;
676 }))
677 .WillOnce(Invoke([&]([[maybe_unused]] sd_bus_message* m,
678 [[maybe_unused]] char type, void* p) {
679 const char** s = static_cast<const char**>(p);
680 *s = property;
681 // Read the string in the pair (dictionary).
682 return 0;
683 }));
684
685 // std::map
686 EXPECT_CALL(sdbus_mock,
687 sd_bus_message_enter_container(IsNull(), 'a', StrEq("{sv}")))
688 .WillOnce(Return(0));
689
690 // while !at_end()
691 EXPECT_CALL(sdbus_mock, sd_bus_message_at_end(IsNull(), 0))
692 .WillOnce(Return(0))
693 .WillOnce(Return(1)); // So it exits the loop after reading one pair.
694
695 // std::pair
696 EXPECT_CALL(sdbus_mock,
697 sd_bus_message_enter_container(IsNull(), 'e', StrEq("sv")))
698 .WillOnce(Return(0));
699
700 EXPECT_CALL(sdbus_mock,
701 sd_bus_message_verify_type(IsNull(), 'v', StrEq("x")))
702 .WillOnce(Return(0));
703 EXPECT_CALL(sdbus_mock,
704 sd_bus_message_verify_type(IsNull(), 'v', StrEq("d")))
705 .WillOnce(Return(0));
706 EXPECT_CALL(sdbus_mock,
707 sd_bus_message_verify_type(IsNull(), 'v', StrEq("b")))
708 .WillOnce(Return(1));
709 EXPECT_CALL(sdbus_mock,
710 sd_bus_message_enter_container(IsNull(), 'v', StrEq("b")))
711 .WillOnce(Return(0));
712
713 EXPECT_CALL(sdbus_mock, sd_bus_message_read_basic(IsNull(), 'b', NotNull()))
714 .WillOnce(Invoke([&]([[maybe_unused]] sd_bus_message* m,
715 [[maybe_unused]] char type, void* p) {
716 bool* s = static_cast<bool*>(p);
717 *s = asserted;
718 return 0;
719 }));
720
721 EXPECT_CALL(sdbus_mock, sd_bus_message_exit_container(IsNull()))
722 .WillOnce(Return(0)) /* variant. */
723 .WillOnce(Return(0)) /* std::pair */
724 .WillOnce(Return(0)); /* std::map */
725
726 int rv = handleSensorValue(msg, passive);
727 EXPECT_EQ(rv, 0); // It's always 0.
728 bool failed = passive->getFailed();
729 EXPECT_EQ(failed, false);
730 ReadReturn r = passive->read();
731 EXPECT_FALSE(std::isfinite(r.value));
732 }
733
TEST_F(DbusPassiveTestUnaSensorNotAsFailedObj,VerifyAvailableAssert)734 TEST_F(DbusPassiveTestUnaSensorNotAsFailedObj, VerifyAvailableAssert)
735 {
736 // Verifies when a sensor's state goes from unavailble to available
737 // && unavailableAsFailed == false, this sensor remains at OK state.
738 EXPECT_CALL(sdbus_mock, sd_bus_message_ref(IsNull()))
739 .WillOnce(Return(nullptr));
740 sdbusplus::message_t msg(nullptr, &sdbus_mock);
741
742 const char* property = "Available";
743 bool asserted = true;
744 const char* intf = "xyz.openbmc_project.State.Decorator.Availability";
745
746 passive->setAvailable(false);
747 bool failed = passive->getFailed();
748 EXPECT_EQ(failed, false);
749
750 EXPECT_CALL(sdbus_mock, sd_bus_message_read_basic(IsNull(), 's', NotNull()))
751 .WillOnce(Invoke([&]([[maybe_unused]] sd_bus_message* m,
752 [[maybe_unused]] char type, void* p) {
753 const char** s = static_cast<const char**>(p);
754 // Read the first parameter, the string.
755 *s = intf;
756 return 0;
757 }))
758 .WillOnce(Invoke([&]([[maybe_unused]] sd_bus_message* m,
759 [[maybe_unused]] char type, void* p) {
760 const char** s = static_cast<const char**>(p);
761 *s = property;
762 // Read the string in the pair (dictionary).
763 return 0;
764 }));
765
766 // std::map
767 EXPECT_CALL(sdbus_mock,
768 sd_bus_message_enter_container(IsNull(), 'a', StrEq("{sv}")))
769 .WillOnce(Return(0));
770
771 // while !at_end()
772 EXPECT_CALL(sdbus_mock, sd_bus_message_at_end(IsNull(), 0))
773 .WillOnce(Return(0))
774 .WillOnce(Return(1)); // So it exits the loop after reading one pair.
775
776 // std::pair
777 EXPECT_CALL(sdbus_mock,
778 sd_bus_message_enter_container(IsNull(), 'e', StrEq("sv")))
779 .WillOnce(Return(0));
780
781 EXPECT_CALL(sdbus_mock,
782 sd_bus_message_verify_type(IsNull(), 'v', StrEq("x")))
783 .WillOnce(Return(0));
784 EXPECT_CALL(sdbus_mock,
785 sd_bus_message_verify_type(IsNull(), 'v', StrEq("d")))
786 .WillOnce(Return(0));
787 EXPECT_CALL(sdbus_mock,
788 sd_bus_message_verify_type(IsNull(), 'v', StrEq("b")))
789 .WillOnce(Return(1));
790 EXPECT_CALL(sdbus_mock,
791 sd_bus_message_enter_container(IsNull(), 'v', StrEq("b")))
792 .WillOnce(Return(0));
793
794 EXPECT_CALL(sdbus_mock, sd_bus_message_read_basic(IsNull(), 'b', NotNull()))
795 .WillOnce(Invoke([&]([[maybe_unused]] sd_bus_message* m,
796 [[maybe_unused]] char type, void* p) {
797 bool* s = static_cast<bool*>(p);
798 *s = asserted;
799 return 0;
800 }));
801
802 EXPECT_CALL(sdbus_mock, sd_bus_message_exit_container(IsNull()))
803 .WillOnce(Return(0)) /* variant. */
804 .WillOnce(Return(0)) /* std::pair */
805 .WillOnce(Return(0)); /* std::map */
806
807 int rv = handleSensorValue(msg, passive);
808 EXPECT_EQ(rv, 0); // It's always 0.
809 failed = passive->getFailed();
810 EXPECT_EQ(failed, false);
811 }
812
GetPropertiesMax3k(const std::string & service,const std::string & path,SensorProperties * prop)813 void GetPropertiesMax3k([[maybe_unused]] const std::string& service,
814 [[maybe_unused]] const std::string& path,
815 SensorProperties* prop)
816 {
817 prop->scale = -3;
818 prop->value = 10;
819 prop->unit = "x";
820 prop->min = 0;
821 prop->max = 3000;
822 }
823
824 using GetPropertiesFunction = std::function<void(
825 const std::string&, const std::string&, SensorProperties*)>;
826
827 // TODO: There is definitely a cleaner way to do this.
828 class DbusPassiveTest3kMaxObj : public ::testing::Test
829 {
830 protected:
DbusPassiveTest3kMaxObj()831 DbusPassiveTest3kMaxObj() :
832 sdbus_mock(),
833 bus_mock(std::move(sdbusplus::get_mocked_new(&sdbus_mock))),
834 helper(std::make_unique<DbusHelperMock>())
835 {
836 EXPECT_CALL(*helper, getService(StrEq(SensorIntf), StrEq(path)))
837 .WillOnce(Return("asdf"));
838
839 EXPECT_CALL(*helper,
840 getProperties(StrEq("asdf"), StrEq(path), NotNull()))
841 .WillOnce(_getProps);
842 EXPECT_CALL(*helper, thresholdsAsserted(StrEq("asdf"), StrEq(path)))
843 .WillOnce(Return(false));
844
845 auto info = conf::SensorConfig();
846 ri = DbusPassive::createDbusPassive(bus_mock, type, id,
847 std::move(helper), &info, nullptr);
848 passive = reinterpret_cast<DbusPassive*>(ri.get());
849 EXPECT_FALSE(passive == nullptr);
850 }
851
852 sdbusplus::SdBusMock sdbus_mock;
853 sdbusplus::bus_t bus_mock;
854 std::unique_ptr<DbusHelperMock> helper;
855 std::string type = "temp";
856 std::string id = "id";
857 std::string path = "/xyz/openbmc_project/sensors/temperature/id";
858 int64_t _scale = -3;
859 int64_t _value = 10;
860
861 std::unique_ptr<ReadInterface> ri;
862 DbusPassive* passive;
863 GetPropertiesFunction _getProps = &GetPropertiesMax3k;
864 };
865
TEST_F(DbusPassiveTest3kMaxObj,ReadMinAndMaxReturnsExpected)866 TEST_F(DbusPassiveTest3kMaxObj, ReadMinAndMaxReturnsExpected)
867 {
868 EXPECT_DOUBLE_EQ(0, passive->getMin());
869 EXPECT_DOUBLE_EQ(3, passive->getMax());
870 }
871
872 class DbusPassiveTest3kMaxIgnoredObj : public ::testing::Test
873 {
874 protected:
DbusPassiveTest3kMaxIgnoredObj()875 DbusPassiveTest3kMaxIgnoredObj() :
876 sdbus_mock(),
877 bus_mock(std::move(sdbusplus::get_mocked_new(&sdbus_mock))),
878 helper(std::make_unique<DbusHelperMock>())
879 {
880 EXPECT_CALL(*helper, getService(StrEq(SensorIntf), StrEq(path)))
881 .WillOnce(Return("asdf"));
882
883 EXPECT_CALL(*helper,
884 getProperties(StrEq("asdf"), StrEq(path), NotNull()))
885 .WillOnce(_getProps);
886 EXPECT_CALL(*helper, thresholdsAsserted(StrEq("asdf"), StrEq(path)))
887 .WillOnce(Return(false));
888
889 auto info = conf::SensorConfig();
890 info.ignoreDbusMinMax = true;
891 ri = DbusPassive::createDbusPassive(bus_mock, type, id,
892 std::move(helper), &info, nullptr);
893 passive = reinterpret_cast<DbusPassive*>(ri.get());
894 EXPECT_FALSE(passive == nullptr);
895 }
896
897 sdbusplus::SdBusMock sdbus_mock;
898 sdbusplus::bus_t bus_mock;
899 std::unique_ptr<DbusHelperMock> helper;
900 std::string type = "temp";
901 std::string id = "id";
902 std::string path = "/xyz/openbmc_project/sensors/temperature/id";
903 int64_t _scale = -3;
904 int64_t _value = 10;
905
906 std::unique_ptr<ReadInterface> ri;
907 DbusPassive* passive;
908 GetPropertiesFunction _getProps = &GetPropertiesMax3k;
909 };
910
TEST_F(DbusPassiveTest3kMaxIgnoredObj,ReadMinAndMaxReturnsExpected)911 TEST_F(DbusPassiveTest3kMaxIgnoredObj, ReadMinAndMaxReturnsExpected)
912 {
913 EXPECT_DOUBLE_EQ(0, passive->getMin());
914 EXPECT_DOUBLE_EQ(0, passive->getMax());
915 }
916
917 } // namespace
918 } // namespace pid_control
919