1 #include "pid/ec/logging.hpp"
2 #include "pid/ec/pid.hpp"
3 #include "pid/zone.hpp"
4 #include "sensors/manager.hpp"
5 #include "test/controller_mock.hpp"
6 #include "test/helpers.hpp"
7 #include "test/sensor_mock.hpp"
8 
9 #include <sdbusplus/test/sdbus_mock.hpp>
10 
11 #include <chrono>
12 #include <cstring>
13 #include <vector>
14 
15 #include <gmock/gmock.h>
16 #include <gtest/gtest.h>
17 
18 namespace pid_control
19 {
20 namespace
21 {
22 
23 using ::testing::_;
24 using ::testing::IsNull;
25 using ::testing::Return;
26 using ::testing::StrEq;
27 
28 static std::string modeInterface = "xyz.openbmc_project.Control.Mode";
29 static std::string enableInterface = "xyz.openbmc_project.Object.Enable";
30 
31 namespace
32 {
33 
34 TEST(PidZoneConstructorTest, BoringConstructorTest)
35 {
36     // Build a PID Zone.
37 
38     sdbusplus::SdBusMock sdbus_mock_passive, sdbus_mock_host, sdbus_mock_mode,
39         sdbus_mock_enable;
40     auto bus_mock_passive = sdbusplus::get_mocked_new(&sdbus_mock_passive);
41     auto bus_mock_host = sdbusplus::get_mocked_new(&sdbus_mock_host);
42     auto bus_mock_mode = sdbusplus::get_mocked_new(&sdbus_mock_mode);
43     auto bus_mock_enable = sdbusplus::get_mocked_new(&sdbus_mock_enable);
44 
45     EXPECT_CALL(sdbus_mock_host,
46                 sd_bus_add_object_manager(
47                     IsNull(), _, StrEq("/xyz/openbmc_project/extsensors")))
48         .WillOnce(Return(0));
49 
50     SensorManager m(bus_mock_passive, bus_mock_host);
51 
52     bool defer = true;
53     const char* objPath = "/path/";
54     int64_t zone = 1;
55     double minThermalOutput = 1000.0;
56     double failSafePercent = 0.75;
57     conf::CycleTime cycleTime;
58 
59     double d;
60     std::vector<std::string> properties;
61     SetupDbusObject(&sdbus_mock_mode, defer, objPath, modeInterface, properties,
62                     &d);
63 
64     std::string sensorname = "temp1";
65     std::string pidsensorpath = "/xyz/openbmc_project/settings/fanctrl/zone1/" +
66                                 sensorname;
67 
68     double de;
69     std::vector<std::string> propertiesenable;
70     SetupDbusObject(&sdbus_mock_enable, defer, pidsensorpath.c_str(),
71                     enableInterface, propertiesenable, &de);
72 
73     DbusPidZone p(zone, minThermalOutput, failSafePercent, cycleTime, m,
74                   bus_mock_mode, objPath, defer);
75     // Success.
76 }
77 
78 } // namespace
79 
80 class PidZoneTest : public ::testing::Test
81 {
82   protected:
83     PidZoneTest() :
84         property_index(), properties(), sdbus_mock_passive(), sdbus_mock_host(),
85         sdbus_mock_mode(), sdbus_mock_enable()
86     {
87         EXPECT_CALL(sdbus_mock_host,
88                     sd_bus_add_object_manager(
89                         IsNull(), _, StrEq("/xyz/openbmc_project/extsensors")))
90             .WillOnce(Return(0));
91 
92         auto bus_mock_passive = sdbusplus::get_mocked_new(&sdbus_mock_passive);
93         auto bus_mock_host = sdbusplus::get_mocked_new(&sdbus_mock_host);
94         auto bus_mock_mode = sdbusplus::get_mocked_new(&sdbus_mock_mode);
95         auto bus_mock_enable = sdbusplus::get_mocked_new(&sdbus_mock_enable);
96 
97         // Compiler weirdly not happy about just instantiating mgr(...);
98         SensorManager m(bus_mock_passive, bus_mock_host);
99         mgr = std::move(m);
100 
101         SetupDbusObject(&sdbus_mock_mode, defer, objPath, modeInterface,
102                         properties, &property_index);
103 
104         SetupDbusObject(&sdbus_mock_enable, defer, pidsensorpath.c_str(),
105                         enableInterface, propertiesenable,
106                         &propertyenable_index);
107 
108         zone = std::make_unique<DbusPidZone>(zoneId, minThermalOutput,
109                                              failSafePercent, cycleTime, mgr,
110                                              bus_mock_mode, objPath, defer);
111     }
112 
113     // unused
114     double property_index;
115     std::vector<std::string> properties;
116     double propertyenable_index;
117     std::vector<std::string> propertiesenable;
118 
119     sdbusplus::SdBusMock sdbus_mock_passive;
120     sdbusplus::SdBusMock sdbus_mock_host;
121     sdbusplus::SdBusMock sdbus_mock_mode;
122     sdbusplus::SdBusMock sdbus_mock_enable;
123     int64_t zoneId = 1;
124     double minThermalOutput = 1000.0;
125     double failSafePercent = 0.75;
126     bool defer = true;
127     const char* objPath = "/path/";
128     SensorManager mgr;
129     conf::CycleTime cycleTime;
130 
131     std::string sensorname = "temp1";
132     std::string pidsensorpath = "/xyz/openbmc_project/settings/fanctrl/zone1/" +
133                                 sensorname;
134 
135     std::unique_ptr<DbusPidZone> zone;
136 };
137 
138 TEST_F(PidZoneTest, GetZoneId_ReturnsExpected)
139 {
140     // Verifies the zoneId returned is what we expect.
141 
142     EXPECT_EQ(zoneId, zone->getZoneID());
143 }
144 
145 TEST_F(PidZoneTest, GetAndSetManualModeTest_BehavesAsExpected)
146 {
147     // Verifies that the zone starts in manual mode.  Verifies that one can set
148     // the mode.
149     EXPECT_FALSE(zone->getManualMode());
150 
151     zone->setManualMode(true);
152     EXPECT_TRUE(zone->getManualMode());
153 }
154 
155 TEST_F(PidZoneTest, AddPidControlProcessGetAndSetEnableTest_BehavesAsExpected)
156 {
157     // Verifies that the zone starts in enable mode.  Verifies that one can set
158     // enable the mode.
159     auto bus_mock_enable = sdbusplus::get_mocked_new(&sdbus_mock_enable);
160 
161     EXPECT_CALL(sdbus_mock_mode, sd_bus_emit_properties_changed_strv(
162                                      IsNull(), StrEq(pidsensorpath.c_str()),
163                                      StrEq(enableInterface), NotNull()))
164         .Times(::testing::AnyNumber())
165         .WillOnce(Invoke(
166             [&]([[maybe_unused]] sd_bus* bus, [[maybe_unused]] const char* path,
167                 [[maybe_unused]] const char* interface, const char** names) {
168         EXPECT_STREQ("Enable", names[0]);
169         return 0;
170         }));
171 
172     zone->addPidControlProcess(sensorname, bus_mock_enable,
173                                pidsensorpath.c_str(), defer);
174     EXPECT_TRUE(zone->isPidProcessEnabled(sensorname));
175 }
176 
177 TEST_F(PidZoneTest, SetManualMode_RedundantWritesEnabledOnceAfterManualMode)
178 {
179     // Tests adding a fan PID controller to the zone, and verifies it's
180     // touched during processing.
181 
182     std::unique_ptr<PIDController> tpid =
183         std::make_unique<ControllerMock>("fan1", zone.get());
184     ControllerMock* tmock = reinterpret_cast<ControllerMock*>(tpid.get());
185 
186     // Access the internal pid configuration to clear it out (unrelated to the
187     // test).
188     ec::pid_info_t* info = tpid->getPIDInfo();
189     std::memset(info, 0x00, sizeof(ec::pid_info_t));
190 
191     zone->addFanPID(std::move(tpid));
192 
193     EXPECT_CALL(*tmock, setptProc()).WillOnce(Return(10.0));
194     EXPECT_CALL(*tmock, inputProc()).WillOnce(Return(11.0));
195     EXPECT_CALL(*tmock, outputProc(_));
196 
197     // while zone is in auto mode redundant writes should be disabled
198     EXPECT_FALSE(zone->getRedundantWrite());
199 
200     // but switching from manual to auto enables a single redundant write
201     zone->setManualMode(true);
202     zone->setManualMode(false);
203     EXPECT_TRUE(zone->getRedundantWrite());
204 
205     // after one iteration of a pid loop redundant write should be cleared
206     zone->processFans();
207     EXPECT_FALSE(zone->getRedundantWrite());
208 }
209 
210 TEST_F(PidZoneTest, RpmSetPoints_AddMaxClear_BehaveAsExpected)
211 {
212     // Tests addSetPoint, clearSetPoints, determineMaxSetPointRequest
213     // and getMinThermalSetPoint.
214 
215     // Need to add pid control process for the zone that can enable
216     // the process and add the set point.
217     auto bus_mock_enable = sdbusplus::get_mocked_new(&sdbus_mock_enable);
218 
219     EXPECT_CALL(sdbus_mock_mode, sd_bus_emit_properties_changed_strv(
220                                      IsNull(), StrEq(pidsensorpath.c_str()),
221                                      StrEq(enableInterface), NotNull()))
222         .Times(::testing::AnyNumber())
223         .WillOnce(Invoke(
224             [&]([[maybe_unused]] sd_bus* bus, [[maybe_unused]] const char* path,
225                 [[maybe_unused]] const char* interface, const char** names) {
226         EXPECT_STREQ("Enable", names[0]);
227         return 0;
228         }));
229 
230     zone->addPidControlProcess(sensorname, bus_mock_enable,
231                                pidsensorpath.c_str(), defer);
232 
233     // At least one value must be above the minimum thermal setpoint used in
234     // the constructor otherwise it'll choose that value
235     std::vector<double> values = {100, 200, 300, 400, 500, 5000};
236 
237     for (auto v : values)
238     {
239         zone->addSetPoint(v, sensorname);
240     }
241 
242     // This will pull the maximum RPM setpoint request.
243     zone->determineMaxSetPointRequest();
244     EXPECT_EQ(5000, zone->getMaxSetPointRequest());
245 
246     // Clear the values, so it'll choose the minimum thermal setpoint.
247     zone->clearSetPoints();
248 
249     // This will go through the RPM set point values and grab the maximum.
250     zone->determineMaxSetPointRequest();
251     EXPECT_EQ(zone->getMinThermalSetPoint(), zone->getMaxSetPointRequest());
252 }
253 
254 TEST_F(PidZoneTest, RpmSetPoints_AddBelowMinimum_BehavesAsExpected)
255 {
256     // Tests adding several RPM setpoints, however, they're all lower than the
257     // configured minimal thermal setpoint RPM value.
258 
259     // Need to add pid control process for the zone that can enable
260     // the process and add the set point.
261     auto bus_mock_enable = sdbusplus::get_mocked_new(&sdbus_mock_enable);
262 
263     EXPECT_CALL(sdbus_mock_mode, sd_bus_emit_properties_changed_strv(
264                                      IsNull(), StrEq(pidsensorpath.c_str()),
265                                      StrEq(enableInterface), NotNull()))
266         .Times(::testing::AnyNumber())
267         .WillOnce(Invoke(
268             [&]([[maybe_unused]] sd_bus* bus, [[maybe_unused]] const char* path,
269                 [[maybe_unused]] const char* interface, const char** names) {
270         EXPECT_STREQ("Enable", names[0]);
271         return 0;
272         }));
273 
274     zone->addPidControlProcess(sensorname, bus_mock_enable,
275                                pidsensorpath.c_str(), defer);
276 
277     std::vector<double> values = {100, 200, 300, 400, 500};
278 
279     for (auto v : values)
280     {
281         zone->addSetPoint(v, sensorname);
282     }
283 
284     // This will pull the maximum RPM setpoint request.
285     zone->determineMaxSetPointRequest();
286 
287     // Verifies the value returned in the minimal thermal rpm set point.
288     EXPECT_EQ(zone->getMinThermalSetPoint(), zone->getMaxSetPointRequest());
289 }
290 
291 TEST_F(PidZoneTest, GetFailSafePercent_ReturnsExpected)
292 {
293     // Verify the value used to create the object is stored.
294     EXPECT_EQ(failSafePercent, zone->getFailSafePercent());
295 }
296 
297 TEST_F(PidZoneTest, ThermalInputs_FailsafeToValid_ReadsSensors)
298 {
299     // This test will add a couple thermal inputs, and verify that the zone
300     // initializes into failsafe mode, and will read each sensor.
301 
302     std::string name1 = "temp1";
303     int64_t timeout = 1;
304 
305     std::unique_ptr<Sensor> sensor1 = std::make_unique<SensorMock>(name1,
306                                                                    timeout);
307     SensorMock* sensor_ptr1 = reinterpret_cast<SensorMock*>(sensor1.get());
308 
309     std::string name2 = "temp2";
310     std::unique_ptr<Sensor> sensor2 = std::make_unique<SensorMock>(name2,
311                                                                    timeout);
312     SensorMock* sensor_ptr2 = reinterpret_cast<SensorMock*>(sensor2.get());
313 
314     std::string type = "unchecked";
315     mgr.addSensor(type, name1, std::move(sensor1));
316     EXPECT_EQ(mgr.getSensor(name1), sensor_ptr1);
317     mgr.addSensor(type, name2, std::move(sensor2));
318     EXPECT_EQ(mgr.getSensor(name2), sensor_ptr2);
319 
320     // Now that the sensors exist, add them to the zone.
321     zone->addThermalInput(name1);
322     zone->addThermalInput(name2);
323 
324     // Initialize Zone
325     zone->initializeCache();
326 
327     // Verify now in failsafe mode.
328     EXPECT_TRUE(zone->getFailSafeMode());
329 
330     ReadReturn r1;
331     r1.value = 10.0;
332     r1.updated = std::chrono::high_resolution_clock::now();
333     EXPECT_CALL(*sensor_ptr1, read()).WillOnce(Return(r1));
334 
335     ReadReturn r2;
336     r2.value = 11.0;
337     r2.updated = std::chrono::high_resolution_clock::now();
338     EXPECT_CALL(*sensor_ptr2, read()).WillOnce(Return(r2));
339 
340     // Read the sensors, this will put the values into the cache.
341     zone->updateSensors();
342 
343     // We should no longer be in failsafe mode.
344     EXPECT_FALSE(zone->getFailSafeMode());
345 
346     EXPECT_EQ(r1.value, zone->getCachedValue(name1));
347     EXPECT_EQ(r2.value, zone->getCachedValue(name2));
348 }
349 
350 TEST_F(PidZoneTest, FanInputTest_VerifiesFanValuesCached)
351 {
352     // This will add a couple fan inputs, and verify the values are cached.
353 
354     std::string name1 = "fan1";
355     int64_t timeout = 2;
356 
357     std::unique_ptr<Sensor> sensor1 = std::make_unique<SensorMock>(name1,
358                                                                    timeout);
359     SensorMock* sensor_ptr1 = reinterpret_cast<SensorMock*>(sensor1.get());
360 
361     std::string name2 = "fan2";
362     std::unique_ptr<Sensor> sensor2 = std::make_unique<SensorMock>(name2,
363                                                                    timeout);
364     SensorMock* sensor_ptr2 = reinterpret_cast<SensorMock*>(sensor2.get());
365 
366     std::string type = "unchecked";
367     mgr.addSensor(type, name1, std::move(sensor1));
368     EXPECT_EQ(mgr.getSensor(name1), sensor_ptr1);
369     mgr.addSensor(type, name2, std::move(sensor2));
370     EXPECT_EQ(mgr.getSensor(name2), sensor_ptr2);
371 
372     // Now that the sensors exist, add them to the zone.
373     zone->addFanInput(name1);
374     zone->addFanInput(name2);
375 
376     // Initialize Zone
377     zone->initializeCache();
378 
379     ReadReturn r1;
380     r1.value = 10.0;
381     r1.updated = std::chrono::high_resolution_clock::now();
382     EXPECT_CALL(*sensor_ptr1, read()).WillOnce(Return(r1));
383 
384     ReadReturn r2;
385     r2.value = 11.0;
386     r2.updated = std::chrono::high_resolution_clock::now();
387     EXPECT_CALL(*sensor_ptr2, read()).WillOnce(Return(r2));
388 
389     // Method under test will read through each fan sensor for the zone and
390     // cache the values.
391     zone->updateFanTelemetry();
392 
393     EXPECT_EQ(r1.value, zone->getCachedValue(name1));
394     EXPECT_EQ(r2.value, zone->getCachedValue(name2));
395 }
396 
397 TEST_F(PidZoneTest, ThermalInput_ValueTimeoutEntersFailSafeMode)
398 {
399     // On the second updateSensors call, the updated timestamp will be beyond
400     // the timeout limit.
401 
402     int64_t timeout = 1;
403 
404     std::string name1 = "temp1";
405     std::unique_ptr<Sensor> sensor1 = std::make_unique<SensorMock>(name1,
406                                                                    timeout);
407     SensorMock* sensor_ptr1 = reinterpret_cast<SensorMock*>(sensor1.get());
408 
409     std::string name2 = "temp2";
410     std::unique_ptr<Sensor> sensor2 = std::make_unique<SensorMock>(name2,
411                                                                    timeout);
412     SensorMock* sensor_ptr2 = reinterpret_cast<SensorMock*>(sensor2.get());
413 
414     std::string type = "unchecked";
415     mgr.addSensor(type, name1, std::move(sensor1));
416     EXPECT_EQ(mgr.getSensor(name1), sensor_ptr1);
417     mgr.addSensor(type, name2, std::move(sensor2));
418     EXPECT_EQ(mgr.getSensor(name2), sensor_ptr2);
419 
420     zone->addThermalInput(name1);
421     zone->addThermalInput(name2);
422 
423     // Initialize Zone
424     zone->initializeCache();
425 
426     // Verify now in failsafe mode.
427     EXPECT_TRUE(zone->getFailSafeMode());
428 
429     ReadReturn r1;
430     r1.value = 10.0;
431     r1.updated = std::chrono::high_resolution_clock::now();
432     EXPECT_CALL(*sensor_ptr1, read()).WillOnce(Return(r1));
433 
434     ReadReturn r2;
435     r2.value = 11.0;
436     r2.updated = std::chrono::high_resolution_clock::now();
437     EXPECT_CALL(*sensor_ptr2, read()).WillOnce(Return(r2));
438 
439     zone->updateSensors();
440     EXPECT_FALSE(zone->getFailSafeMode());
441 
442     // Ok, so we're not in failsafe mode, so let's set updated to the past.
443     // sensor1 will have an updated field older than its timeout value, but
444     // sensor2 will be fine. :D
445     r1.updated -= std::chrono::seconds(3);
446     r2.updated = std::chrono::high_resolution_clock::now();
447 
448     EXPECT_CALL(*sensor_ptr1, read()).WillOnce(Return(r1));
449     EXPECT_CALL(*sensor_ptr2, read()).WillOnce(Return(r2));
450 
451     // Method under test will read each sensor.  One sensor's value is older
452     // than the timeout for that sensor and this triggers failsafe mode.
453     zone->updateSensors();
454     EXPECT_TRUE(zone->getFailSafeMode());
455 }
456 
457 TEST_F(PidZoneTest, FanInputTest_FailsafeToValid_ReadsSensors)
458 {
459     // This will add a couple fan inputs, and verify the values are cached.
460 
461     std::string name1 = "fan1";
462     int64_t timeout = 2;
463 
464     std::unique_ptr<Sensor> sensor1 = std::make_unique<SensorMock>(name1,
465                                                                    timeout);
466     SensorMock* sensor_ptr1 = reinterpret_cast<SensorMock*>(sensor1.get());
467 
468     std::string name2 = "fan2";
469     std::unique_ptr<Sensor> sensor2 = std::make_unique<SensorMock>(name2,
470                                                                    timeout);
471     SensorMock* sensor_ptr2 = reinterpret_cast<SensorMock*>(sensor2.get());
472 
473     std::string type = "unchecked";
474     mgr.addSensor(type, name1, std::move(sensor1));
475     EXPECT_EQ(mgr.getSensor(name1), sensor_ptr1);
476     mgr.addSensor(type, name2, std::move(sensor2));
477     EXPECT_EQ(mgr.getSensor(name2), sensor_ptr2);
478 
479     // Now that the sensors exist, add them to the zone.
480     zone->addFanInput(name1);
481     zone->addFanInput(name2);
482 
483     // Initialize Zone
484     zone->initializeCache();
485 
486     // Verify now in failsafe mode.
487     EXPECT_TRUE(zone->getFailSafeMode());
488 
489     ReadReturn r1;
490     r1.value = 10.0;
491     r1.updated = std::chrono::high_resolution_clock::now();
492     EXPECT_CALL(*sensor_ptr1, read()).WillOnce(Return(r1));
493 
494     ReadReturn r2;
495     r2.value = 11.0;
496     r2.updated = std::chrono::high_resolution_clock::now();
497     EXPECT_CALL(*sensor_ptr2, read()).WillOnce(Return(r2));
498 
499     // Method under test will read through each fan sensor for the zone and
500     // cache the values.
501     zone->updateFanTelemetry();
502 
503     // We should no longer be in failsafe mode.
504     EXPECT_FALSE(zone->getFailSafeMode());
505 
506     EXPECT_EQ(r1.value, zone->getCachedValue(name1));
507     EXPECT_EQ(r2.value, zone->getCachedValue(name2));
508 }
509 
510 TEST_F(PidZoneTest, FanInputTest_ValueTimeoutEntersFailSafeMode)
511 {
512     // This will add a couple fan inputs, and verify the values are cached.
513 
514     std::string name1 = "fan1";
515     int64_t timeout = 2;
516 
517     std::unique_ptr<Sensor> sensor1 = std::make_unique<SensorMock>(name1,
518                                                                    timeout);
519     SensorMock* sensor_ptr1 = reinterpret_cast<SensorMock*>(sensor1.get());
520 
521     std::string name2 = "fan2";
522     std::unique_ptr<Sensor> sensor2 = std::make_unique<SensorMock>(name2,
523                                                                    timeout);
524     SensorMock* sensor_ptr2 = reinterpret_cast<SensorMock*>(sensor2.get());
525 
526     std::string type = "unchecked";
527     mgr.addSensor(type, name1, std::move(sensor1));
528     EXPECT_EQ(mgr.getSensor(name1), sensor_ptr1);
529     mgr.addSensor(type, name2, std::move(sensor2));
530     EXPECT_EQ(mgr.getSensor(name2), sensor_ptr2);
531 
532     // Now that the sensors exist, add them to the zone.
533     zone->addFanInput(name1);
534     zone->addFanInput(name2);
535 
536     // Initialize Zone
537     zone->initializeCache();
538 
539     // Verify now in failsafe mode.
540     EXPECT_TRUE(zone->getFailSafeMode());
541 
542     ReadReturn r1;
543     r1.value = 10.0;
544     r1.updated = std::chrono::high_resolution_clock::now();
545     EXPECT_CALL(*sensor_ptr1, read()).WillOnce(Return(r1));
546 
547     ReadReturn r2;
548     r2.value = 11.0;
549     r2.updated = std::chrono::high_resolution_clock::now();
550     EXPECT_CALL(*sensor_ptr2, read()).WillOnce(Return(r2));
551 
552     // Method under test will read through each fan sensor for the zone and
553     // cache the values.
554     zone->updateFanTelemetry();
555 
556     // We should no longer be in failsafe mode.
557     EXPECT_FALSE(zone->getFailSafeMode());
558 
559     r1.updated -= std::chrono::seconds(3);
560     r2.updated = std::chrono::high_resolution_clock::now();
561 
562     EXPECT_CALL(*sensor_ptr1, read()).WillOnce(Return(r1));
563     EXPECT_CALL(*sensor_ptr2, read()).WillOnce(Return(r2));
564 
565     zone->updateFanTelemetry();
566     EXPECT_TRUE(zone->getFailSafeMode());
567 }
568 
569 TEST_F(PidZoneTest, GetSensorTest_ReturnsExpected)
570 {
571     // One can grab a sensor from the manager through the zone.
572 
573     int64_t timeout = 1;
574 
575     std::string name1 = "temp1";
576     std::unique_ptr<Sensor> sensor1 = std::make_unique<SensorMock>(name1,
577                                                                    timeout);
578     SensorMock* sensor_ptr1 = reinterpret_cast<SensorMock*>(sensor1.get());
579 
580     std::string type = "unchecked";
581     mgr.addSensor(type, name1, std::move(sensor1));
582     EXPECT_EQ(mgr.getSensor(name1), sensor_ptr1);
583 
584     zone->addThermalInput(name1);
585 
586     // Verify method under test returns the pointer we expect.
587     EXPECT_EQ(mgr.getSensor(name1), zone->getSensor(name1));
588 }
589 
590 TEST_F(PidZoneTest, AddThermalPIDTest_VerifiesThermalPIDsProcessed)
591 {
592     // Tests adding a thermal PID controller to the zone, and verifies it's
593     // touched during processing.
594 
595     std::unique_ptr<PIDController> tpid =
596         std::make_unique<ControllerMock>("thermal1", zone.get());
597     ControllerMock* tmock = reinterpret_cast<ControllerMock*>(tpid.get());
598 
599     // Access the internal pid configuration to clear it out (unrelated to the
600     // test).
601     ec::pid_info_t* info = tpid->getPIDInfo();
602     std::memset(info, 0x00, sizeof(ec::pid_info_t));
603 
604     zone->addThermalPID(std::move(tpid));
605 
606     EXPECT_CALL(*tmock, setptProc()).WillOnce(Return(10.0));
607     EXPECT_CALL(*tmock, inputProc()).WillOnce(Return(11.0));
608     EXPECT_CALL(*tmock, outputProc(_));
609 
610     // Method under test will, for each thermal PID, call setpt, input, and
611     // output.
612     zone->processThermals();
613 }
614 
615 TEST_F(PidZoneTest, AddFanPIDTest_VerifiesFanPIDsProcessed)
616 {
617     // Tests adding a fan PID controller to the zone, and verifies it's
618     // touched during processing.
619 
620     std::unique_ptr<PIDController> tpid =
621         std::make_unique<ControllerMock>("fan1", zone.get());
622     ControllerMock* tmock = reinterpret_cast<ControllerMock*>(tpid.get());
623 
624     // Access the internal pid configuration to clear it out (unrelated to the
625     // test).
626     ec::pid_info_t* info = tpid->getPIDInfo();
627     std::memset(info, 0x00, sizeof(ec::pid_info_t));
628 
629     zone->addFanPID(std::move(tpid));
630 
631     EXPECT_CALL(*tmock, setptProc()).WillOnce(Return(10.0));
632     EXPECT_CALL(*tmock, inputProc()).WillOnce(Return(11.0));
633     EXPECT_CALL(*tmock, outputProc(_));
634 
635     // Method under test will, for each fan PID, call setpt, input, and output.
636     zone->processFans();
637 }
638 
639 TEST_F(PidZoneTest, ManualModeDbusTest_VerifySetManualBehavesAsExpected)
640 {
641     // The manual(bool) method is inherited from the dbus mode interface.
642 
643     // Verifies that someone doesn't remove the internal call to the dbus
644     // object from which we're inheriting.
645     EXPECT_CALL(sdbus_mock_mode,
646                 sd_bus_emit_properties_changed_strv(
647                     IsNull(), StrEq(objPath), StrEq(modeInterface), NotNull()))
648         .WillOnce(Invoke(
649             [&]([[maybe_unused]] sd_bus* bus, [[maybe_unused]] const char* path,
650                 [[maybe_unused]] const char* interface, const char** names) {
651         EXPECT_STREQ("Manual", names[0]);
652         return 0;
653         }));
654 
655     // Method under test will set the manual mode to true and broadcast this
656     // change on dbus.
657     zone->manual(true);
658     EXPECT_TRUE(zone->getManualMode());
659 }
660 
661 TEST_F(PidZoneTest, FailsafeDbusTest_VerifiesReturnsExpected)
662 {
663     // This property is implemented by us as read-only, such that trying to
664     // write to it will have no effect.
665     EXPECT_EQ(zone->failSafe(), zone->getFailSafeMode());
666 }
667 
668 } // namespace
669 } // namespace pid_control
670