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