blob: 4eb348700b7246b2595097c81b3beeb7f6a12b25 [file] [log] [blame]
Josh Lehande745422020-11-07 02:14:09 -08001#include "pid/ec/logging.hpp"
Patrick Ventureda4a5dd2018-08-31 09:42:48 -07002#include "pid/ec/pid.hpp"
Patrick Venturea58197c2018-06-11 15:29:45 -07003#include "pid/zone.hpp"
Patrick Ventureda4a5dd2018-08-31 09:42:48 -07004#include "sensors/manager.hpp"
5#include "test/controller_mock.hpp"
6#include "test/helpers.hpp"
7#include "test/sensor_mock.hpp"
Patrick Venturea58197c2018-06-11 15:29:45 -07008
Patrick Venturea83a3ec2020-08-04 09:52:05 -07009#include <sdbusplus/test/sdbus_mock.hpp>
10
Patrick Venturea58197c2018-06-11 15:29:45 -070011#include <chrono>
12#include <cstring>
Patrick Venturea58197c2018-06-11 15:29:45 -070013#include <vector>
14
Patrick Ventureda4a5dd2018-08-31 09:42:48 -070015#include <gmock/gmock.h>
16#include <gtest/gtest.h>
Patrick Venturea58197c2018-06-11 15:29:45 -070017
Patrick Venturea0764872020-08-08 07:48:43 -070018namespace pid_control
19{
20namespace
21{
22
Patrick Ventureda4a5dd2018-08-31 09:42:48 -070023using ::testing::_;
Patrick Venturea58197c2018-06-11 15:29:45 -070024using ::testing::IsNull;
25using ::testing::Return;
26using ::testing::StrEq;
Patrick Venturea58197c2018-06-11 15:29:45 -070027
28static std::string modeInterface = "xyz.openbmc_project.Control.Mode";
ykchiu7c6d35d2023-05-10 17:01:46 +080029static std::string enableInterface = "xyz.openbmc_project.Object.Enable";
Patrick Venturea58197c2018-06-11 15:29:45 -070030
Patrick Ventureda4a5dd2018-08-31 09:42:48 -070031namespace
32{
Patrick Venturea58197c2018-06-11 15:29:45 -070033
Patrick Ventureda4a5dd2018-08-31 09:42:48 -070034TEST(PidZoneConstructorTest, BoringConstructorTest)
35{
Patrick Venturea58197c2018-06-11 15:29:45 -070036 // Build a PID Zone.
37
ykchiu7c6d35d2023-05-10 17:01:46 +080038 sdbusplus::SdBusMock sdbus_mock_passive, sdbus_mock_host, sdbus_mock_mode,
39 sdbus_mock_enable;
Patrick Venturea58197c2018-06-11 15:29:45 -070040 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);
ykchiu7c6d35d2023-05-10 17:01:46 +080043 auto bus_mock_enable = sdbusplus::get_mocked_new(&sdbus_mock_enable);
Patrick Venturea58197c2018-06-11 15:29:45 -070044
45 EXPECT_CALL(sdbus_mock_host,
46 sd_bus_add_object_manager(
Patrick Ventureda4a5dd2018-08-31 09:42:48 -070047 IsNull(), _, StrEq("/xyz/openbmc_project/extsensors")))
Patrick Venturea58197c2018-06-11 15:29:45 -070048 .WillOnce(Return(0));
49
James Feist1fe08952019-05-07 09:17:16 -070050 SensorManager m(bus_mock_passive, bus_mock_host);
Patrick Venturea58197c2018-06-11 15:29:45 -070051
52 bool defer = true;
Patrick Venturee2ec0f62018-09-04 12:30:27 -070053 const char* objPath = "/path/";
Patrick Venturea58197c2018-06-11 15:29:45 -070054 int64_t zone = 1;
James Feist3484bed2019-02-25 13:28:18 -080055 double minThermalOutput = 1000.0;
Patrick Venture5f59c0f2018-11-11 12:55:14 -080056 double failSafePercent = 0.75;
Bonnie Lo0e8fc392022-10-05 10:20:55 +080057 conf::CycleTime cycleTime;
Patrick Venturea58197c2018-06-11 15:29:45 -070058
James Feist0709e2f2020-07-08 10:59:45 -070059 double d;
Patrick Venturea58197c2018-06-11 15:29:45 -070060 std::vector<std::string> properties;
Patrick Ventureda4a5dd2018-08-31 09:42:48 -070061 SetupDbusObject(&sdbus_mock_mode, defer, objPath, modeInterface, properties,
James Feist0709e2f2020-07-08 10:59:45 -070062 &d);
Patrick Venturea58197c2018-06-11 15:29:45 -070063
ykchiu7c6d35d2023-05-10 17:01:46 +080064 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
Bonnie Lo0e8fc392022-10-05 10:20:55 +080073 DbusPidZone p(zone, minThermalOutput, failSafePercent, cycleTime, m,
74 bus_mock_mode, objPath, defer);
Patrick Venturea58197c2018-06-11 15:29:45 -070075 // Success.
76}
77
Patrick Ventureda4a5dd2018-08-31 09:42:48 -070078} // namespace
Patrick Venturea58197c2018-06-11 15:29:45 -070079
Patrick Ventureda4a5dd2018-08-31 09:42:48 -070080class PidZoneTest : public ::testing::Test
81{
82 protected:
83 PidZoneTest() :
84 property_index(), properties(), sdbus_mock_passive(), sdbus_mock_host(),
ykchiu7c6d35d2023-05-10 17:01:46 +080085 sdbus_mock_mode(), sdbus_mock_enable()
Patrick Ventureda4a5dd2018-08-31 09:42:48 -070086 {
87 EXPECT_CALL(sdbus_mock_host,
88 sd_bus_add_object_manager(
89 IsNull(), _, StrEq("/xyz/openbmc_project/extsensors")))
90 .WillOnce(Return(0));
Patrick Venturea58197c2018-06-11 15:29:45 -070091
Patrick Ventureda4a5dd2018-08-31 09:42:48 -070092 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);
ykchiu7c6d35d2023-05-10 17:01:46 +080095 auto bus_mock_enable = sdbusplus::get_mocked_new(&sdbus_mock_enable);
Patrick Venturea58197c2018-06-11 15:29:45 -070096
Patrick Ventureda4a5dd2018-08-31 09:42:48 -070097 // Compiler weirdly not happy about just instantiating mgr(...);
James Feist1fe08952019-05-07 09:17:16 -070098 SensorManager m(bus_mock_passive, bus_mock_host);
Patrick Ventureda4a5dd2018-08-31 09:42:48 -070099 mgr = std::move(m);
Patrick Venturea58197c2018-06-11 15:29:45 -0700100
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700101 SetupDbusObject(&sdbus_mock_mode, defer, objPath, modeInterface,
102 properties, &property_index);
Patrick Venturea58197c2018-06-11 15:29:45 -0700103
ykchiu7c6d35d2023-05-10 17:01:46 +0800104 SetupDbusObject(&sdbus_mock_enable, defer, pidsensorpath.c_str(),
105 enableInterface, propertiesenable,
106 &propertyenable_index);
107
Patrick Venture597ebd62020-08-11 08:48:19 -0700108 zone = std::make_unique<DbusPidZone>(zoneId, minThermalOutput,
Bonnie Lo0e8fc392022-10-05 10:20:55 +0800109 failSafePercent, cycleTime, mgr,
Patrick Venture597ebd62020-08-11 08:48:19 -0700110 bus_mock_mode, objPath, defer);
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700111 }
Patrick Venturea58197c2018-06-11 15:29:45 -0700112
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700113 // unused
James Feist0709e2f2020-07-08 10:59:45 -0700114 double property_index;
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700115 std::vector<std::string> properties;
ykchiu7c6d35d2023-05-10 17:01:46 +0800116 double propertyenable_index;
117 std::vector<std::string> propertiesenable;
Patrick Venturea58197c2018-06-11 15:29:45 -0700118
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700119 sdbusplus::SdBusMock sdbus_mock_passive;
120 sdbusplus::SdBusMock sdbus_mock_host;
121 sdbusplus::SdBusMock sdbus_mock_mode;
ykchiu7c6d35d2023-05-10 17:01:46 +0800122 sdbusplus::SdBusMock sdbus_mock_enable;
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700123 int64_t zoneId = 1;
James Feist3484bed2019-02-25 13:28:18 -0800124 double minThermalOutput = 1000.0;
Patrick Venture5f59c0f2018-11-11 12:55:14 -0800125 double failSafePercent = 0.75;
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700126 bool defer = true;
Patrick Venturee2ec0f62018-09-04 12:30:27 -0700127 const char* objPath = "/path/";
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700128 SensorManager mgr;
Bonnie Lo0e8fc392022-10-05 10:20:55 +0800129 conf::CycleTime cycleTime;
Patrick Venturea58197c2018-06-11 15:29:45 -0700130
ykchiu7c6d35d2023-05-10 17:01:46 +0800131 std::string sensorname = "temp1";
132 std::string pidsensorpath = "/xyz/openbmc_project/settings/fanctrl/zone1/" +
133 sensorname;
134
Patrick Venture597ebd62020-08-11 08:48:19 -0700135 std::unique_ptr<DbusPidZone> zone;
Patrick Venturea58197c2018-06-11 15:29:45 -0700136};
137
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700138TEST_F(PidZoneTest, GetZoneId_ReturnsExpected)
139{
Patrick Venturea58197c2018-06-11 15:29:45 -0700140 // Verifies the zoneId returned is what we expect.
141
Patrick Venture0bbeaf82018-10-30 18:50:31 -0700142 EXPECT_EQ(zoneId, zone->getZoneID());
Patrick Venturea58197c2018-06-11 15:29:45 -0700143}
144
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700145TEST_F(PidZoneTest, GetAndSetManualModeTest_BehavesAsExpected)
146{
Patrick Venturea58197c2018-06-11 15:29:45 -0700147 // 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
ykchiu7c6d35d2023-05-10 17:01:46 +0800155TEST_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
Josh Lehana4146eb2020-10-01 11:49:09 -0700177TEST_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
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700210TEST_F(PidZoneTest, RpmSetPoints_AddMaxClear_BehaveAsExpected)
211{
Patrick Venturef7a2dd52019-07-16 14:31:13 -0700212 // Tests addSetPoint, clearSetPoints, determineMaxSetPointRequest
Nirav Shahccc8bb62022-02-17 21:06:51 -0800213 // and getMinThermalSetPoint.
Patrick Venturea58197c2018-06-11 15:29:45 -0700214
ykchiu7c6d35d2023-05-10 17:01:46 +0800215 // 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
Patrick Venturea58197c2018-06-11 15:29:45 -0700233 // At least one value must be above the minimum thermal setpoint used in
234 // the constructor otherwise it'll choose that value
Patrick Venture5f59c0f2018-11-11 12:55:14 -0800235 std::vector<double> values = {100, 200, 300, 400, 500, 5000};
ykchiu7c6d35d2023-05-10 17:01:46 +0800236
Patrick Venturea58197c2018-06-11 15:29:45 -0700237 for (auto v : values)
238 {
ykchiu7c6d35d2023-05-10 17:01:46 +0800239 zone->addSetPoint(v, sensorname);
Patrick Venturea58197c2018-06-11 15:29:45 -0700240 }
241
242 // This will pull the maximum RPM setpoint request.
Patrick Venturef7a2dd52019-07-16 14:31:13 -0700243 zone->determineMaxSetPointRequest();
244 EXPECT_EQ(5000, zone->getMaxSetPointRequest());
Patrick Venturea58197c2018-06-11 15:29:45 -0700245
246 // Clear the values, so it'll choose the minimum thermal setpoint.
Patrick Venture9bbf3332019-07-16 10:50:37 -0700247 zone->clearSetPoints();
Patrick Venturea58197c2018-06-11 15:29:45 -0700248
249 // This will go through the RPM set point values and grab the maximum.
Patrick Venturef7a2dd52019-07-16 14:31:13 -0700250 zone->determineMaxSetPointRequest();
Nirav Shahccc8bb62022-02-17 21:06:51 -0800251 EXPECT_EQ(zone->getMinThermalSetPoint(), zone->getMaxSetPointRequest());
Patrick Venturea58197c2018-06-11 15:29:45 -0700252}
253
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700254TEST_F(PidZoneTest, RpmSetPoints_AddBelowMinimum_BehavesAsExpected)
255{
Patrick Venturea58197c2018-06-11 15:29:45 -0700256 // Tests adding several RPM setpoints, however, they're all lower than the
Patrick Venture7280e272019-02-11 10:45:32 -0800257 // configured minimal thermal setpoint RPM value.
Patrick Venturea58197c2018-06-11 15:29:45 -0700258
ykchiu7c6d35d2023-05-10 17:01:46 +0800259 // 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
Patrick Venture5f59c0f2018-11-11 12:55:14 -0800277 std::vector<double> values = {100, 200, 300, 400, 500};
ykchiu7c6d35d2023-05-10 17:01:46 +0800278
Patrick Venturea58197c2018-06-11 15:29:45 -0700279 for (auto v : values)
280 {
ykchiu7c6d35d2023-05-10 17:01:46 +0800281 zone->addSetPoint(v, sensorname);
Patrick Venturea58197c2018-06-11 15:29:45 -0700282 }
283
284 // This will pull the maximum RPM setpoint request.
Patrick Venturef7a2dd52019-07-16 14:31:13 -0700285 zone->determineMaxSetPointRequest();
Patrick Venturea58197c2018-06-11 15:29:45 -0700286
287 // Verifies the value returned in the minimal thermal rpm set point.
Nirav Shahccc8bb62022-02-17 21:06:51 -0800288 EXPECT_EQ(zone->getMinThermalSetPoint(), zone->getMaxSetPointRequest());
Patrick Venturea58197c2018-06-11 15:29:45 -0700289}
290
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700291TEST_F(PidZoneTest, GetFailSafePercent_ReturnsExpected)
292{
Patrick Venturea58197c2018-06-11 15:29:45 -0700293 // Verify the value used to create the object is stored.
294 EXPECT_EQ(failSafePercent, zone->getFailSafePercent());
295}
296
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700297TEST_F(PidZoneTest, ThermalInputs_FailsafeToValid_ReadsSensors)
298{
Patrick Venturea58197c2018-06-11 15:29:45 -0700299 // 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
Patrick Williams8c051122023-05-10 07:50:59 -0500305 std::unique_ptr<Sensor> sensor1 = std::make_unique<SensorMock>(name1,
306 timeout);
Patrick Venturee2ec0f62018-09-04 12:30:27 -0700307 SensorMock* sensor_ptr1 = reinterpret_cast<SensorMock*>(sensor1.get());
Patrick Venturea58197c2018-06-11 15:29:45 -0700308
309 std::string name2 = "temp2";
Patrick Williams8c051122023-05-10 07:50:59 -0500310 std::unique_ptr<Sensor> sensor2 = std::make_unique<SensorMock>(name2,
311 timeout);
Patrick Venturee2ec0f62018-09-04 12:30:27 -0700312 SensorMock* sensor_ptr2 = reinterpret_cast<SensorMock*>(sensor2.get());
Patrick Venturea58197c2018-06-11 15:29:45 -0700313
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
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700350TEST_F(PidZoneTest, FanInputTest_VerifiesFanValuesCached)
351{
Patrick Venturea58197c2018-06-11 15:29:45 -0700352 // 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
Patrick Williams8c051122023-05-10 07:50:59 -0500357 std::unique_ptr<Sensor> sensor1 = std::make_unique<SensorMock>(name1,
358 timeout);
Patrick Venturee2ec0f62018-09-04 12:30:27 -0700359 SensorMock* sensor_ptr1 = reinterpret_cast<SensorMock*>(sensor1.get());
Patrick Venturea58197c2018-06-11 15:29:45 -0700360
361 std::string name2 = "fan2";
Patrick Williams8c051122023-05-10 07:50:59 -0500362 std::unique_ptr<Sensor> sensor2 = std::make_unique<SensorMock>(name2,
363 timeout);
Patrick Venturee2ec0f62018-09-04 12:30:27 -0700364 SensorMock* sensor_ptr2 = reinterpret_cast<SensorMock*>(sensor2.get());
Patrick Venturea58197c2018-06-11 15:29:45 -0700365
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
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700397TEST_F(PidZoneTest, ThermalInput_ValueTimeoutEntersFailSafeMode)
398{
Patrick Venturea58197c2018-06-11 15:29:45 -0700399 // 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";
Patrick Williams8c051122023-05-10 07:50:59 -0500405 std::unique_ptr<Sensor> sensor1 = std::make_unique<SensorMock>(name1,
406 timeout);
Patrick Venturee2ec0f62018-09-04 12:30:27 -0700407 SensorMock* sensor_ptr1 = reinterpret_cast<SensorMock*>(sensor1.get());
Patrick Venturea58197c2018-06-11 15:29:45 -0700408
409 std::string name2 = "temp2";
Patrick Williams8c051122023-05-10 07:50:59 -0500410 std::unique_ptr<Sensor> sensor2 = std::make_unique<SensorMock>(name2,
411 timeout);
Patrick Venturee2ec0f62018-09-04 12:30:27 -0700412 SensorMock* sensor_ptr2 = reinterpret_cast<SensorMock*>(sensor2.get());
Patrick Venturea58197c2018-06-11 15:29:45 -0700413
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
Will Liangded0ab52019-05-15 17:10:06 +0800457TEST_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
Patrick Williams8c051122023-05-10 07:50:59 -0500464 std::unique_ptr<Sensor> sensor1 = std::make_unique<SensorMock>(name1,
465 timeout);
Will Liangded0ab52019-05-15 17:10:06 +0800466 SensorMock* sensor_ptr1 = reinterpret_cast<SensorMock*>(sensor1.get());
467
468 std::string name2 = "fan2";
Patrick Williams8c051122023-05-10 07:50:59 -0500469 std::unique_ptr<Sensor> sensor2 = std::make_unique<SensorMock>(name2,
470 timeout);
Will Liangded0ab52019-05-15 17:10:06 +0800471 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
510TEST_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
Patrick Williams8c051122023-05-10 07:50:59 -0500517 std::unique_ptr<Sensor> sensor1 = std::make_unique<SensorMock>(name1,
518 timeout);
Will Liangded0ab52019-05-15 17:10:06 +0800519 SensorMock* sensor_ptr1 = reinterpret_cast<SensorMock*>(sensor1.get());
520
521 std::string name2 = "fan2";
Patrick Williams8c051122023-05-10 07:50:59 -0500522 std::unique_ptr<Sensor> sensor2 = std::make_unique<SensorMock>(name2,
523 timeout);
Will Liangded0ab52019-05-15 17:10:06 +0800524 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
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700569TEST_F(PidZoneTest, GetSensorTest_ReturnsExpected)
570{
Patrick Venturea58197c2018-06-11 15:29:45 -0700571 // One can grab a sensor from the manager through the zone.
572
573 int64_t timeout = 1;
574
575 std::string name1 = "temp1";
Patrick Williams8c051122023-05-10 07:50:59 -0500576 std::unique_ptr<Sensor> sensor1 = std::make_unique<SensorMock>(name1,
577 timeout);
Patrick Venturee2ec0f62018-09-04 12:30:27 -0700578 SensorMock* sensor_ptr1 = reinterpret_cast<SensorMock*>(sensor1.get());
Patrick Venturea58197c2018-06-11 15:29:45 -0700579
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
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700590TEST_F(PidZoneTest, AddThermalPIDTest_VerifiesThermalPIDsProcessed)
591{
Patrick Venturea58197c2018-06-11 15:29:45 -0700592 // 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());
Patrick Venturee2ec0f62018-09-04 12:30:27 -0700597 ControllerMock* tmock = reinterpret_cast<ControllerMock*>(tpid.get());
Patrick Venturea58197c2018-06-11 15:29:45 -0700598
599 // Access the internal pid configuration to clear it out (unrelated to the
600 // test).
Patrick Venture563a3562018-10-30 09:31:26 -0700601 ec::pid_info_t* info = tpid->getPIDInfo();
Patrick Venturea58197c2018-06-11 15:29:45 -0700602 std::memset(info, 0x00, sizeof(ec::pid_info_t));
603
604 zone->addThermalPID(std::move(tpid));
605
Patrick Venture563a3562018-10-30 09:31:26 -0700606 EXPECT_CALL(*tmock, setptProc()).WillOnce(Return(10.0));
607 EXPECT_CALL(*tmock, inputProc()).WillOnce(Return(11.0));
608 EXPECT_CALL(*tmock, outputProc(_));
Patrick Venturea58197c2018-06-11 15:29:45 -0700609
610 // Method under test will, for each thermal PID, call setpt, input, and
611 // output.
Patrick Venture563a3562018-10-30 09:31:26 -0700612 zone->processThermals();
Patrick Venturea58197c2018-06-11 15:29:45 -0700613}
614
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700615TEST_F(PidZoneTest, AddFanPIDTest_VerifiesFanPIDsProcessed)
616{
Patrick Venturea58197c2018-06-11 15:29:45 -0700617 // 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());
Patrick Venturee2ec0f62018-09-04 12:30:27 -0700622 ControllerMock* tmock = reinterpret_cast<ControllerMock*>(tpid.get());
Patrick Venturea58197c2018-06-11 15:29:45 -0700623
624 // Access the internal pid configuration to clear it out (unrelated to the
625 // test).
Patrick Venture563a3562018-10-30 09:31:26 -0700626 ec::pid_info_t* info = tpid->getPIDInfo();
Patrick Venturea58197c2018-06-11 15:29:45 -0700627 std::memset(info, 0x00, sizeof(ec::pid_info_t));
628
629 zone->addFanPID(std::move(tpid));
630
Patrick Venture563a3562018-10-30 09:31:26 -0700631 EXPECT_CALL(*tmock, setptProc()).WillOnce(Return(10.0));
632 EXPECT_CALL(*tmock, inputProc()).WillOnce(Return(11.0));
633 EXPECT_CALL(*tmock, outputProc(_));
Patrick Venturea58197c2018-06-11 15:29:45 -0700634
635 // Method under test will, for each fan PID, call setpt, input, and output.
Patrick Venture563a3562018-10-30 09:31:26 -0700636 zone->processFans();
Patrick Venturea58197c2018-06-11 15:29:45 -0700637}
638
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700639TEST_F(PidZoneTest, ManualModeDbusTest_VerifySetManualBehavesAsExpected)
640{
Patrick Venturea58197c2018-06-11 15:29:45 -0700641 // 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,
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700646 sd_bus_emit_properties_changed_strv(
647 IsNull(), StrEq(objPath), StrEq(modeInterface), NotNull()))
Harvey.Wua1ae4fa2022-10-28 17:38:35 +0800648 .WillOnce(Invoke(
649 [&]([[maybe_unused]] sd_bus* bus, [[maybe_unused]] const char* path,
650 [[maybe_unused]] const char* interface, const char** names) {
Patrick Williams8c051122023-05-10 07:50:59 -0500651 EXPECT_STREQ("Manual", names[0]);
652 return 0;
653 }));
Patrick Venturea58197c2018-06-11 15:29:45 -0700654
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
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700661TEST_F(PidZoneTest, FailsafeDbusTest_VerifiesReturnsExpected)
662{
Patrick Venturea58197c2018-06-11 15:29:45 -0700663 // 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}
Patrick Venturea0764872020-08-08 07:48:43 -0700667
668} // namespace
669} // namespace pid_control