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 debugZoneInterface = "xyz.openbmc_project.Debug.Pid.Zone";
30 static std::string enableInterface = "xyz.openbmc_project.Object.Enable";
31 static std::string debugThermalPowerInterface =
32     "xyz.openbmc_project.Debug.Pid.ThermalPower";
33 
34 namespace
35 {
36 
TEST(PidZoneConstructorTest,BoringConstructorTest)37 TEST(PidZoneConstructorTest, BoringConstructorTest)
38 {
39     // Build a PID Zone.
40 
41     sdbusplus::SdBusMock sdbus_mock_passive, sdbus_mock_host, sdbus_mock_mode,
42         sdbus_mock_enable;
43     auto bus_mock_passive = sdbusplus::get_mocked_new(&sdbus_mock_passive);
44     auto bus_mock_host = sdbusplus::get_mocked_new(&sdbus_mock_host);
45     auto bus_mock_mode = sdbusplus::get_mocked_new(&sdbus_mock_mode);
46     auto bus_mock_enable = sdbusplus::get_mocked_new(&sdbus_mock_enable);
47 
48     EXPECT_CALL(sdbus_mock_host,
49                 sd_bus_add_object_manager(
50                     IsNull(), _, StrEq("/xyz/openbmc_project/extsensors")))
51         .WillOnce(Return(0));
52 
53     SensorManager m(bus_mock_passive, bus_mock_host);
54 
55     bool defer = true;
56     bool accSetPoint = false;
57     const char* objPath = "/path/";
58     int64_t zone = 1;
59     double minThermalOutput = 1000.0;
60     double failSafePercent = 100;
61     conf::CycleTime cycleTime;
62 
63     double d;
64     std::vector<std::string> properties;
65     SetupDbusObject(&sdbus_mock_mode, defer, objPath, modeInterface, properties,
66                     &d);
67     SetupDbusObject(&sdbus_mock_mode, defer, objPath, debugZoneInterface,
68                     properties, &d);
69 
70     std::string sensorname = "temp1";
71     std::string pidsensorpath =
72         "/xyz/openbmc_project/settings/fanctrl/zone1/" + sensorname;
73 
74     double de;
75     std::vector<std::string> propertiesenable;
76     SetupDbusObject(&sdbus_mock_enable, defer, pidsensorpath.c_str(),
77                     enableInterface, propertiesenable, &de);
78 
79     DbusPidZone p(zone, minThermalOutput, failSafePercent, cycleTime, m,
80                   bus_mock_mode, objPath, defer, accSetPoint);
81     // Success.
82 }
83 
84 } // namespace
85 
86 class PidZoneTest : public ::testing::Test
87 {
88   protected:
PidZoneTest()89     PidZoneTest() :
90         property_index(), properties(), sdbus_mock_passive(), sdbus_mock_host(),
91         sdbus_mock_mode(), sdbus_mock_enable()
92     {
93         EXPECT_CALL(sdbus_mock_host,
94                     sd_bus_add_object_manager(
95                         IsNull(), _, StrEq("/xyz/openbmc_project/extsensors")))
96             .WillOnce(Return(0));
97 
98         auto bus_mock_passive = sdbusplus::get_mocked_new(&sdbus_mock_passive);
99         auto bus_mock_host = sdbusplus::get_mocked_new(&sdbus_mock_host);
100         auto bus_mock_mode = sdbusplus::get_mocked_new(&sdbus_mock_mode);
101         auto bus_mock_enable = sdbusplus::get_mocked_new(&sdbus_mock_enable);
102 
103         // Compiler weirdly not happy about just instantiating mgr(...);
104         SensorManager m(bus_mock_passive, bus_mock_host);
105         mgr = std::move(m);
106 
107         SetupDbusObject(&sdbus_mock_mode, defer, objPath, modeInterface,
108                         properties, &property_index);
109         SetupDbusObject(&sdbus_mock_mode, defer, objPath, debugZoneInterface,
110                         properties, &property_index);
111 
112         SetupDbusObject(&sdbus_mock_enable, defer, pidsensorpath.c_str(),
113                         enableInterface, propertiesenable,
114                         &propertyenable_index);
115 
116         zone = std::make_unique<DbusPidZone>(
117             zoneId, minThermalOutput, failSafePercent, cycleTime, mgr,
118             bus_mock_mode, objPath, defer, accSetPoint);
119     }
120 
121     // unused
122     double property_index;
123     std::vector<std::string> properties;
124     double propertyenable_index;
125     std::vector<std::string> propertiesenable;
126 
127     sdbusplus::SdBusMock sdbus_mock_passive;
128     sdbusplus::SdBusMock sdbus_mock_host;
129     sdbusplus::SdBusMock sdbus_mock_mode;
130     sdbusplus::SdBusMock sdbus_mock_enable;
131     int64_t zoneId = 1;
132     double minThermalOutput = 1000.0;
133     double failSafePercent = 100;
134     double setpoint = 50.0;
135     bool defer = true;
136     bool accSetPoint = false;
137     const char* objPath = "/path/";
138     SensorManager mgr;
139     conf::CycleTime cycleTime;
140 
141     std::string sensorname = "temp1";
142     std::string sensorType = "temp";
143     std::string pidsensorpath =
144         "/xyz/openbmc_project/settings/fanctrl/zone1/" + sensorname;
145 
146     std::unique_ptr<DbusPidZone> zone;
147 };
148 
TEST_F(PidZoneTest,GetZoneId_ReturnsExpected)149 TEST_F(PidZoneTest, GetZoneId_ReturnsExpected)
150 {
151     // Verifies the zoneId returned is what we expect.
152 
153     EXPECT_EQ(zoneId, zone->getZoneID());
154 }
155 
TEST_F(PidZoneTest,GetAndSetManualModeTest_BehavesAsExpected)156 TEST_F(PidZoneTest, GetAndSetManualModeTest_BehavesAsExpected)
157 {
158     // Verifies that the zone starts in manual mode.  Verifies that one can set
159     // the mode.
160     EXPECT_FALSE(zone->getManualMode());
161 
162     zone->setManualMode(true);
163     EXPECT_TRUE(zone->getManualMode());
164 }
165 
TEST_F(PidZoneTest,AddPidControlProcessGetAndSetEnableTest_BehavesAsExpected)166 TEST_F(PidZoneTest, AddPidControlProcessGetAndSetEnableTest_BehavesAsExpected)
167 {
168     // Verifies that the zone starts in enable mode.  Verifies that one can set
169     // enable the mode.
170     auto bus_mock_enable = sdbusplus::get_mocked_new(&sdbus_mock_enable);
171 
172     EXPECT_CALL(sdbus_mock_mode, sd_bus_emit_properties_changed_strv(
173                                      IsNull(), StrEq(pidsensorpath.c_str()),
174                                      StrEq(enableInterface), NotNull()))
175         .Times(::testing::AnyNumber())
176         .WillOnce(Invoke(
177             [&]([[maybe_unused]] sd_bus* bus, [[maybe_unused]] const char* path,
178                 [[maybe_unused]] const char* interface, const char** names) {
179                 EXPECT_STREQ("Enable", names[0]);
180                 return 0;
181             }));
182 
183     zone->addPidControlProcess(sensorname, sensorType, setpoint,
184                                bus_mock_enable, pidsensorpath.c_str(), defer);
185     EXPECT_TRUE(zone->isPidProcessEnabled(sensorname));
186 }
187 
TEST_F(PidZoneTest,SetManualMode_RedundantWritesEnabledOnceAfterManualMode)188 TEST_F(PidZoneTest, SetManualMode_RedundantWritesEnabledOnceAfterManualMode)
189 {
190     // Tests adding a fan PID controller to the zone, and verifies it's
191     // touched during processing.
192 
193     std::unique_ptr<PIDController> tpid =
194         std::make_unique<ControllerMock>("fan1", zone.get());
195     ControllerMock* tmock = reinterpret_cast<ControllerMock*>(tpid.get());
196 
197     // Access the internal pid configuration to clear it out (unrelated to the
198     // test).
199     [[maybe_unused]] ec::pid_info_t* info = tpid->getPIDInfo();
200 
201     zone->addFanPID(std::move(tpid));
202 
203     EXPECT_CALL(*tmock, setptProc()).WillOnce(Return(10.0));
204     EXPECT_CALL(*tmock, inputProc()).WillOnce(Return(11.0));
205     EXPECT_CALL(*tmock, outputProc(_));
206 
207     // while zone is in auto mode redundant writes should be disabled
208     EXPECT_FALSE(zone->getRedundantWrite());
209 
210     // but switching from manual to auto enables a single redundant write
211     zone->setManualMode(true);
212     zone->setManualMode(false);
213     EXPECT_TRUE(zone->getRedundantWrite());
214 
215     // after one iteration of a pid loop redundant write should be cleared
216     zone->processFans();
217     EXPECT_FALSE(zone->getRedundantWrite());
218 }
219 
TEST_F(PidZoneTest,RpmSetPoints_AddMaxClear_BehaveAsExpected)220 TEST_F(PidZoneTest, RpmSetPoints_AddMaxClear_BehaveAsExpected)
221 {
222     // Tests addSetPoint, clearSetPoints, determineMaxSetPointRequest
223     // and getMinThermalSetPoint.
224 
225     // Need to add pid control process for the zone that can enable
226     // the process and add the set point.
227     auto bus_mock_enable = sdbusplus::get_mocked_new(&sdbus_mock_enable);
228 
229     EXPECT_CALL(sdbus_mock_mode, sd_bus_emit_properties_changed_strv(
230                                      IsNull(), StrEq(pidsensorpath.c_str()),
231                                      StrEq(enableInterface), NotNull()))
232         .Times(::testing::AnyNumber())
233         .WillOnce(Invoke(
234             [&]([[maybe_unused]] sd_bus* bus, [[maybe_unused]] const char* path,
235                 [[maybe_unused]] const char* interface, const char** names) {
236                 EXPECT_STREQ("Enable", names[0]);
237                 return 0;
238             }));
239 
240     zone->addPidControlProcess(sensorname, sensorType, setpoint,
241                                bus_mock_enable, pidsensorpath.c_str(), defer);
242 
243     // At least one value must be above the minimum thermal setpoint used in
244     // the constructor otherwise it'll choose that value
245     std::vector<double> values = {100, 200, 300, 400, 500, 5000};
246 
247     for (auto v : values)
248     {
249         zone->addSetPoint(v, sensorname);
250     }
251 
252     // This will pull the maximum RPM setpoint request.
253     zone->determineMaxSetPointRequest();
254     EXPECT_EQ(5000, zone->getMaxSetPointRequest());
255 
256     // Clear the values, so it'll choose the minimum thermal setpoint.
257     zone->clearSetPoints();
258 
259     // This will go through the RPM set point values and grab the maximum.
260     zone->determineMaxSetPointRequest();
261     EXPECT_EQ(zone->getMinThermalSetPoint(), zone->getMaxSetPointRequest());
262 }
263 
TEST_F(PidZoneTest,RpmSetPoints_AddBelowMinimum_BehavesAsExpected)264 TEST_F(PidZoneTest, RpmSetPoints_AddBelowMinimum_BehavesAsExpected)
265 {
266     // Tests adding several RPM setpoints, however, they're all lower than the
267     // configured minimal thermal setpoint RPM value.
268 
269     // Need to add pid control process for the zone that can enable
270     // the process and add the set point.
271     auto bus_mock_enable = sdbusplus::get_mocked_new(&sdbus_mock_enable);
272 
273     EXPECT_CALL(sdbus_mock_mode, sd_bus_emit_properties_changed_strv(
274                                      IsNull(), StrEq(pidsensorpath.c_str()),
275                                      StrEq(enableInterface), NotNull()))
276         .Times(::testing::AnyNumber())
277         .WillOnce(Invoke(
278             [&]([[maybe_unused]] sd_bus* bus, [[maybe_unused]] const char* path,
279                 [[maybe_unused]] const char* interface, const char** names) {
280                 EXPECT_STREQ("Enable", names[0]);
281                 return 0;
282             }));
283 
284     zone->addPidControlProcess(sensorname, sensorType, setpoint,
285                                bus_mock_enable, pidsensorpath.c_str(), defer);
286 
287     std::vector<double> values = {100, 200, 300, 400, 500};
288 
289     for (auto v : values)
290     {
291         zone->addSetPoint(v, sensorname);
292     }
293 
294     // This will pull the maximum RPM setpoint request.
295     zone->determineMaxSetPointRequest();
296 
297     // Verifies the value returned in the minimal thermal rpm set point.
298     EXPECT_EQ(zone->getMinThermalSetPoint(), zone->getMaxSetPointRequest());
299 }
300 
TEST_F(PidZoneTest,GetFailSafePercent_SingleFailedReturnsExpected)301 TEST_F(PidZoneTest, GetFailSafePercent_SingleFailedReturnsExpected)
302 {
303     // Tests when only one sensor failed and the sensor's failsafe duty is zero,
304     // and verify that the sensor name is empty and failsafe duty is PID zone's
305     // failsafe duty.
306 
307     std::vector<std::string> input1 = {"temp1"};
308     std::vector<std::string> input2 = {"temp2"};
309     std::vector<std::string> input3 = {"temp3"};
310     std::vector<double> values = {0, 0, 0};
311 
312     zone->addPidFailSafePercent(input1, values[0]);
313     zone->addPidFailSafePercent(input2, values[1]);
314     zone->addPidFailSafePercent(input3, values[2]);
315 
316     zone->markSensorMissing("temp1");
317 
318     EXPECT_EQ(failSafePercent, zone->getFailSafePercent());
319 }
320 
TEST_F(PidZoneTest,GetFailSafePercent_MultiFailedReturnsExpected)321 TEST_F(PidZoneTest, GetFailSafePercent_MultiFailedReturnsExpected)
322 {
323     // Tests when multi sensor failed, and verify the final failsafe's sensor
324     // name and duty as expected.
325 
326     std::vector<std::string> input1 = {"temp1"};
327     std::vector<std::string> input2 = {"temp2"};
328     std::vector<std::string> input3 = {"temp3"};
329     std::vector<double> values = {60, 80, 70};
330 
331     zone->addPidFailSafePercent(input1, values[0]);
332     zone->addPidFailSafePercent(input2, values[1]);
333     zone->addPidFailSafePercent(input3, values[2]);
334 
335     zone->markSensorMissing("temp1");
336     zone->markSensorMissing("temp2");
337     zone->markSensorMissing("temp3");
338 
339     EXPECT_EQ(80, zone->getFailSafePercent());
340 }
341 
TEST_F(PidZoneTest,ThermalInputs_FailsafeToValid_ReadsSensors)342 TEST_F(PidZoneTest, ThermalInputs_FailsafeToValid_ReadsSensors)
343 {
344     // This test will add a couple thermal inputs, and verify that the zone
345     // initializes into failsafe mode, and will read each sensor.
346 
347     std::string name1 = "temp1";
348     int64_t timeout = 1;
349 
350     std::unique_ptr<Sensor> sensor1 =
351         std::make_unique<SensorMock>(name1, timeout);
352     SensorMock* sensor_ptr1 = reinterpret_cast<SensorMock*>(sensor1.get());
353 
354     std::string name2 = "temp2";
355     std::unique_ptr<Sensor> sensor2 =
356         std::make_unique<SensorMock>(name2, timeout);
357     SensorMock* sensor_ptr2 = reinterpret_cast<SensorMock*>(sensor2.get());
358 
359     std::string type = "unchecked";
360     mgr.addSensor(type, name1, std::move(sensor1));
361     EXPECT_EQ(mgr.getSensor(name1), sensor_ptr1);
362     mgr.addSensor(type, name2, std::move(sensor2));
363     EXPECT_EQ(mgr.getSensor(name2), sensor_ptr2);
364 
365     // Now that the sensors exist, add them to the zone.
366     zone->addThermalInput(name1, false);
367     zone->addThermalInput(name2, false);
368 
369     // Initialize Zone
370     zone->initializeCache();
371 
372     // Verify now in failsafe mode.
373     EXPECT_TRUE(zone->getFailSafeMode());
374 
375     ReadReturn r1;
376     r1.value = 10.0;
377     r1.updated = std::chrono::high_resolution_clock::now();
378     EXPECT_CALL(*sensor_ptr1, read()).WillOnce(Return(r1));
379 
380     ReadReturn r2;
381     r2.value = 11.0;
382     r2.updated = std::chrono::high_resolution_clock::now();
383     EXPECT_CALL(*sensor_ptr2, read()).WillOnce(Return(r2));
384 
385     // Read the sensors, this will put the values into the cache.
386     zone->updateSensors();
387 
388     // We should no longer be in failsafe mode.
389     EXPECT_FALSE(zone->getFailSafeMode());
390 
391     EXPECT_EQ(r1.value, zone->getCachedValue(name1));
392     EXPECT_EQ(r2.value, zone->getCachedValue(name2));
393 }
394 
TEST_F(PidZoneTest,FanInputTest_VerifiesFanValuesCached)395 TEST_F(PidZoneTest, FanInputTest_VerifiesFanValuesCached)
396 {
397     // This will add a couple fan inputs, and verify the values are cached.
398 
399     std::string name1 = "fan1";
400     int64_t timeout = 2;
401 
402     std::unique_ptr<Sensor> sensor1 =
403         std::make_unique<SensorMock>(name1, timeout);
404     SensorMock* sensor_ptr1 = reinterpret_cast<SensorMock*>(sensor1.get());
405 
406     std::string name2 = "fan2";
407     std::unique_ptr<Sensor> sensor2 =
408         std::make_unique<SensorMock>(name2, timeout);
409     SensorMock* sensor_ptr2 = reinterpret_cast<SensorMock*>(sensor2.get());
410 
411     std::string type = "unchecked";
412     mgr.addSensor(type, name1, std::move(sensor1));
413     EXPECT_EQ(mgr.getSensor(name1), sensor_ptr1);
414     mgr.addSensor(type, name2, std::move(sensor2));
415     EXPECT_EQ(mgr.getSensor(name2), sensor_ptr2);
416 
417     // Now that the sensors exist, add them to the zone.
418     zone->addFanInput(name1, false);
419     zone->addFanInput(name2, false);
420 
421     // Initialize Zone
422     zone->initializeCache();
423 
424     ReadReturn r1;
425     r1.value = 10.0;
426     r1.updated = std::chrono::high_resolution_clock::now();
427     EXPECT_CALL(*sensor_ptr1, read()).WillOnce(Return(r1));
428 
429     ReadReturn r2;
430     r2.value = 11.0;
431     r2.updated = std::chrono::high_resolution_clock::now();
432     EXPECT_CALL(*sensor_ptr2, read()).WillOnce(Return(r2));
433 
434     // Method under test will read through each fan sensor for the zone and
435     // cache the values.
436     zone->updateFanTelemetry();
437 
438     EXPECT_EQ(r1.value, zone->getCachedValue(name1));
439     EXPECT_EQ(r2.value, zone->getCachedValue(name2));
440 }
441 
TEST_F(PidZoneTest,ThermalInput_ValueTimeoutEntersFailSafeMode)442 TEST_F(PidZoneTest, ThermalInput_ValueTimeoutEntersFailSafeMode)
443 {
444     // On the second updateSensors call, the updated timestamp will be beyond
445     // the timeout limit.
446 
447     int64_t timeout = 1;
448 
449     std::string name1 = "temp1";
450     std::unique_ptr<Sensor> sensor1 =
451         std::make_unique<SensorMock>(name1, timeout);
452     SensorMock* sensor_ptr1 = reinterpret_cast<SensorMock*>(sensor1.get());
453 
454     std::string name2 = "temp2";
455     std::unique_ptr<Sensor> sensor2 =
456         std::make_unique<SensorMock>(name2, timeout);
457     SensorMock* sensor_ptr2 = reinterpret_cast<SensorMock*>(sensor2.get());
458 
459     std::string type = "unchecked";
460     mgr.addSensor(type, name1, std::move(sensor1));
461     EXPECT_EQ(mgr.getSensor(name1), sensor_ptr1);
462     mgr.addSensor(type, name2, std::move(sensor2));
463     EXPECT_EQ(mgr.getSensor(name2), sensor_ptr2);
464 
465     zone->addThermalInput(name1, false);
466     zone->addThermalInput(name2, false);
467 
468     // Initialize Zone
469     zone->initializeCache();
470 
471     // Verify now in failsafe mode.
472     EXPECT_TRUE(zone->getFailSafeMode());
473 
474     ReadReturn r1;
475     r1.value = 10.0;
476     r1.updated = std::chrono::high_resolution_clock::now();
477     EXPECT_CALL(*sensor_ptr1, read()).WillOnce(Return(r1));
478 
479     ReadReturn r2;
480     r2.value = 11.0;
481     r2.updated = std::chrono::high_resolution_clock::now();
482     EXPECT_CALL(*sensor_ptr2, read()).WillOnce(Return(r2));
483 
484     zone->updateSensors();
485     EXPECT_FALSE(zone->getFailSafeMode());
486 
487     // Ok, so we're not in failsafe mode, so let's set updated to the past.
488     // sensor1 will have an updated field older than its timeout value, but
489     // sensor2 will be fine. :D
490     r1.updated -= std::chrono::seconds(3);
491     r2.updated = std::chrono::high_resolution_clock::now();
492 
493     EXPECT_CALL(*sensor_ptr1, read()).WillOnce(Return(r1));
494     EXPECT_CALL(*sensor_ptr2, read()).WillOnce(Return(r2));
495 
496     // Method under test will read each sensor.  One sensor's value is older
497     // than the timeout for that sensor and this triggers failsafe mode.
498     zone->updateSensors();
499     EXPECT_TRUE(zone->getFailSafeMode());
500 }
501 
TEST_F(PidZoneTest,ThermalInput_MissingIsAcceptableNoFailSafe)502 TEST_F(PidZoneTest, ThermalInput_MissingIsAcceptableNoFailSafe)
503 {
504     // This is similar to the above test, but because missingIsAcceptable
505     // is set for sensor1, the zone should not enter failsafe mode when
506     // only sensor1 goes missing.
507     // However, sensor2 going missing should still trigger failsafe mode.
508 
509     int64_t timeout = 1;
510 
511     std::string name1 = "temp1";
512     std::unique_ptr<Sensor> sensor1 =
513         std::make_unique<SensorMock>(name1, timeout);
514     SensorMock* sensor_ptr1 = reinterpret_cast<SensorMock*>(sensor1.get());
515 
516     std::string name2 = "temp2";
517     std::unique_ptr<Sensor> sensor2 =
518         std::make_unique<SensorMock>(name2, timeout);
519     SensorMock* sensor_ptr2 = reinterpret_cast<SensorMock*>(sensor2.get());
520 
521     std::string type = "unchecked";
522     mgr.addSensor(type, name1, std::move(sensor1));
523     EXPECT_EQ(mgr.getSensor(name1), sensor_ptr1);
524     mgr.addSensor(type, name2, std::move(sensor2));
525     EXPECT_EQ(mgr.getSensor(name2), sensor_ptr2);
526 
527     // Only sensor1 has MissingIsAcceptable enabled for it
528     zone->addThermalInput(name1, true);
529     zone->addThermalInput(name2, false);
530 
531     // Initialize Zone
532     zone->initializeCache();
533 
534     // As sensors are not initialized, zone should be in failsafe mode
535     EXPECT_TRUE(zone->getFailSafeMode());
536 
537     // r1 not populated here, intentionally, to simulate a sensor that
538     // is not available yet, perhaps takes a long time to start up.
539     ReadReturn r1;
540     EXPECT_CALL(*sensor_ptr1, read()).WillOnce(Return(r1));
541 
542     ReadReturn r2;
543     r2.value = 11.0;
544     r2.updated = std::chrono::high_resolution_clock::now();
545     EXPECT_CALL(*sensor_ptr2, read()).WillOnce(Return(r2));
546 
547     zone->updateSensors();
548 
549     // Only sensor2 has been initialized here. Failsafe should be false,
550     // because sensor1 MissingIsAcceptable so it is OK for it to go missing.
551     EXPECT_FALSE(zone->getFailSafeMode());
552 
553     r1.value = 10.0;
554     r1.updated = std::chrono::high_resolution_clock::now();
555 
556     EXPECT_CALL(*sensor_ptr1, read()).WillOnce(Return(r1));
557     EXPECT_CALL(*sensor_ptr2, read()).WillOnce(Return(r2));
558     zone->updateSensors();
559 
560     // Both sensors are now properly initialized
561     EXPECT_FALSE(zone->getFailSafeMode());
562 
563     // Ok, so we're not in failsafe mode, so let's set updated to the past.
564     // sensor1 will have an updated field older than its timeout value, but
565     // sensor2 will be fine. :D
566     r1.updated -= std::chrono::seconds(3);
567     r2.updated = std::chrono::high_resolution_clock::now();
568 
569     EXPECT_CALL(*sensor_ptr1, read()).WillOnce(Return(r1));
570     EXPECT_CALL(*sensor_ptr2, read()).WillOnce(Return(r2));
571     zone->updateSensors();
572 
573     // MissingIsAcceptable is true for sensor1, so the zone should not be
574     // thrown into failsafe mode.
575     EXPECT_FALSE(zone->getFailSafeMode());
576 
577     // Do the same thing, but for the opposite sensors: r1 is good,
578     // but r2 is set to some time in the past.
579     r1.updated = std::chrono::high_resolution_clock::now();
580     r2.updated -= std::chrono::seconds(3);
581 
582     EXPECT_CALL(*sensor_ptr1, read()).WillOnce(Return(r1));
583     EXPECT_CALL(*sensor_ptr2, read()).WillOnce(Return(r2));
584     zone->updateSensors();
585 
586     // Now, the zone should be in failsafe mode, because sensor2 does not
587     // have MissingIsAcceptable set true, it is still subject to failsafe.
588     EXPECT_TRUE(zone->getFailSafeMode());
589 
590     r1.updated = std::chrono::high_resolution_clock::now();
591     r2.updated = std::chrono::high_resolution_clock::now();
592 
593     EXPECT_CALL(*sensor_ptr1, read()).WillOnce(Return(r1));
594     EXPECT_CALL(*sensor_ptr2, read()).WillOnce(Return(r2));
595     zone->updateSensors();
596 
597     // The failsafe mode should cease, as both sensors are good again.
598     EXPECT_FALSE(zone->getFailSafeMode());
599 }
600 
TEST_F(PidZoneTest,FanInputTest_FailsafeToValid_ReadsSensors)601 TEST_F(PidZoneTest, FanInputTest_FailsafeToValid_ReadsSensors)
602 {
603     // This will add a couple fan inputs, and verify the values are cached.
604 
605     std::string name1 = "fan1";
606     int64_t timeout = 2;
607 
608     std::unique_ptr<Sensor> sensor1 =
609         std::make_unique<SensorMock>(name1, timeout);
610     SensorMock* sensor_ptr1 = reinterpret_cast<SensorMock*>(sensor1.get());
611 
612     std::string name2 = "fan2";
613     std::unique_ptr<Sensor> sensor2 =
614         std::make_unique<SensorMock>(name2, timeout);
615     SensorMock* sensor_ptr2 = reinterpret_cast<SensorMock*>(sensor2.get());
616 
617     std::string type = "unchecked";
618     mgr.addSensor(type, name1, std::move(sensor1));
619     EXPECT_EQ(mgr.getSensor(name1), sensor_ptr1);
620     mgr.addSensor(type, name2, std::move(sensor2));
621     EXPECT_EQ(mgr.getSensor(name2), sensor_ptr2);
622 
623     // Now that the sensors exist, add them to the zone.
624     zone->addFanInput(name1, false);
625     zone->addFanInput(name2, false);
626 
627     // Initialize Zone
628     zone->initializeCache();
629 
630     // Verify now in failsafe mode.
631     EXPECT_TRUE(zone->getFailSafeMode());
632 
633     ReadReturn r1;
634     r1.value = 10.0;
635     r1.updated = std::chrono::high_resolution_clock::now();
636     EXPECT_CALL(*sensor_ptr1, read()).WillOnce(Return(r1));
637 
638     ReadReturn r2;
639     r2.value = 11.0;
640     r2.updated = std::chrono::high_resolution_clock::now();
641     EXPECT_CALL(*sensor_ptr2, read()).WillOnce(Return(r2));
642 
643     // Method under test will read through each fan sensor for the zone and
644     // cache the values.
645     zone->updateFanTelemetry();
646 
647     // We should no longer be in failsafe mode.
648     EXPECT_FALSE(zone->getFailSafeMode());
649 
650     EXPECT_EQ(r1.value, zone->getCachedValue(name1));
651     EXPECT_EQ(r2.value, zone->getCachedValue(name2));
652 }
653 
TEST_F(PidZoneTest,FanInputTest_ValueTimeoutEntersFailSafeMode)654 TEST_F(PidZoneTest, FanInputTest_ValueTimeoutEntersFailSafeMode)
655 {
656     // This will add a couple fan inputs, and verify the values are cached.
657 
658     std::string name1 = "fan1";
659     int64_t timeout = 2;
660 
661     std::unique_ptr<Sensor> sensor1 =
662         std::make_unique<SensorMock>(name1, timeout);
663     SensorMock* sensor_ptr1 = reinterpret_cast<SensorMock*>(sensor1.get());
664 
665     std::string name2 = "fan2";
666     std::unique_ptr<Sensor> sensor2 =
667         std::make_unique<SensorMock>(name2, timeout);
668     SensorMock* sensor_ptr2 = reinterpret_cast<SensorMock*>(sensor2.get());
669 
670     std::string type = "unchecked";
671     mgr.addSensor(type, name1, std::move(sensor1));
672     EXPECT_EQ(mgr.getSensor(name1), sensor_ptr1);
673     mgr.addSensor(type, name2, std::move(sensor2));
674     EXPECT_EQ(mgr.getSensor(name2), sensor_ptr2);
675 
676     // Now that the sensors exist, add them to the zone.
677     zone->addFanInput(name1, false);
678     zone->addFanInput(name2, false);
679 
680     // Initialize Zone
681     zone->initializeCache();
682 
683     // Verify now in failsafe mode.
684     EXPECT_TRUE(zone->getFailSafeMode());
685 
686     ReadReturn r1;
687     r1.value = 10.0;
688     r1.updated = std::chrono::high_resolution_clock::now();
689     EXPECT_CALL(*sensor_ptr1, read()).WillOnce(Return(r1));
690 
691     ReadReturn r2;
692     r2.value = 11.0;
693     r2.updated = std::chrono::high_resolution_clock::now();
694     EXPECT_CALL(*sensor_ptr2, read()).WillOnce(Return(r2));
695 
696     // Method under test will read through each fan sensor for the zone and
697     // cache the values.
698     zone->updateFanTelemetry();
699 
700     // We should no longer be in failsafe mode.
701     EXPECT_FALSE(zone->getFailSafeMode());
702 
703     r1.updated -= std::chrono::seconds(3);
704     r2.updated = std::chrono::high_resolution_clock::now();
705 
706     EXPECT_CALL(*sensor_ptr1, read()).WillOnce(Return(r1));
707     EXPECT_CALL(*sensor_ptr2, read()).WillOnce(Return(r2));
708 
709     zone->updateFanTelemetry();
710     EXPECT_TRUE(zone->getFailSafeMode());
711 }
712 
TEST_F(PidZoneTest,GetSensorTest_ReturnsExpected)713 TEST_F(PidZoneTest, GetSensorTest_ReturnsExpected)
714 {
715     // One can grab a sensor from the manager through the zone.
716 
717     int64_t timeout = 1;
718 
719     std::string name1 = "temp1";
720     std::unique_ptr<Sensor> sensor1 =
721         std::make_unique<SensorMock>(name1, timeout);
722     SensorMock* sensor_ptr1 = reinterpret_cast<SensorMock*>(sensor1.get());
723 
724     std::string type = "unchecked";
725     mgr.addSensor(type, name1, std::move(sensor1));
726     EXPECT_EQ(mgr.getSensor(name1), sensor_ptr1);
727 
728     zone->addThermalInput(name1, false);
729 
730     // Verify method under test returns the pointer we expect.
731     EXPECT_EQ(mgr.getSensor(name1), zone->getSensor(name1));
732 }
733 
TEST_F(PidZoneTest,AddThermalPIDTest_VerifiesThermalPIDsProcessed)734 TEST_F(PidZoneTest, AddThermalPIDTest_VerifiesThermalPIDsProcessed)
735 {
736     // Tests adding a thermal PID controller to the zone, and verifies it's
737     // touched during processing.
738 
739     std::unique_ptr<PIDController> tpid =
740         std::make_unique<ControllerMock>("thermal1", zone.get());
741     ControllerMock* tmock = reinterpret_cast<ControllerMock*>(tpid.get());
742 
743     // Access the internal pid configuration to clear it out (unrelated to the
744     // test).
745     [[maybe_unused]] ec::pid_info_t* info = tpid->getPIDInfo();
746 
747     zone->addThermalPID(std::move(tpid));
748 
749     EXPECT_CALL(*tmock, setptProc()).WillOnce(Return(10.0));
750     EXPECT_CALL(*tmock, inputProc()).WillOnce(Return(11.0));
751     EXPECT_CALL(*tmock, outputProc(_));
752 
753     // Method under test will, for each thermal PID, call setpt, input, and
754     // output.
755     zone->processThermals();
756 }
757 
TEST_F(PidZoneTest,AddFanPIDTest_VerifiesFanPIDsProcessed)758 TEST_F(PidZoneTest, AddFanPIDTest_VerifiesFanPIDsProcessed)
759 {
760     // Tests adding a fan PID controller to the zone, and verifies it's
761     // touched during processing.
762 
763     std::unique_ptr<PIDController> tpid =
764         std::make_unique<ControllerMock>("fan1", zone.get());
765     ControllerMock* tmock = reinterpret_cast<ControllerMock*>(tpid.get());
766 
767     // Access the internal pid configuration to clear it out (unrelated to the
768     // test).
769     [[maybe_unused]] ec::pid_info_t* info = tpid->getPIDInfo();
770 
771     zone->addFanPID(std::move(tpid));
772 
773     EXPECT_CALL(*tmock, setptProc()).WillOnce(Return(10.0));
774     EXPECT_CALL(*tmock, inputProc()).WillOnce(Return(11.0));
775     EXPECT_CALL(*tmock, outputProc(_));
776 
777     // Method under test will, for each fan PID, call setpt, input, and output.
778     zone->processFans();
779 }
780 
TEST_F(PidZoneTest,ManualModeDbusTest_VerifySetManualBehavesAsExpected)781 TEST_F(PidZoneTest, ManualModeDbusTest_VerifySetManualBehavesAsExpected)
782 {
783     // The manual(bool) method is inherited from the dbus mode interface.
784 
785     // Verifies that someone doesn't remove the internal call to the dbus
786     // object from which we're inheriting.
787     EXPECT_CALL(sdbus_mock_mode,
788                 sd_bus_emit_properties_changed_strv(
789                     IsNull(), StrEq(objPath), StrEq(modeInterface), NotNull()))
790         .WillOnce(Invoke(
791             [&]([[maybe_unused]] sd_bus* bus, [[maybe_unused]] const char* path,
792                 [[maybe_unused]] const char* interface, const char** names) {
793                 EXPECT_STREQ("Manual", names[0]);
794                 return 0;
795             }));
796 
797     // Method under test will set the manual mode to true and broadcast this
798     // change on dbus.
799     zone->manual(true);
800     EXPECT_TRUE(zone->getManualMode());
801 }
802 
TEST_F(PidZoneTest,FailsafeDbusTest_VerifiesReturnsExpected)803 TEST_F(PidZoneTest, FailsafeDbusTest_VerifiesReturnsExpected)
804 {
805     // This property is implemented by us as read-only, such that trying to
806     // write to it will have no effect.
807     EXPECT_EQ(zone->failSafe(), zone->getFailSafeMode());
808 }
809 
810 } // namespace
811 } // namespace pid_control
812