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