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