blob: 391d026e2b0df738c6201c2042679d1215abf69e [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";
Harvey Wucc0232a2023-02-09 14:58:55 +080029static std::string debugZoneInterface = "xyz.openbmc_project.Debug.Pid.Zone";
ykchiu7c6d35d2023-05-10 17:01:46 +080030static std::string enableInterface = "xyz.openbmc_project.Object.Enable";
Patrick Venturea58197c2018-06-11 15:29:45 -070031
Patrick Ventureda4a5dd2018-08-31 09:42:48 -070032namespace
33{
Patrick Venturea58197c2018-06-11 15:29:45 -070034
Patrick Ventureda4a5dd2018-08-31 09:42:48 -070035TEST(PidZoneConstructorTest, BoringConstructorTest)
36{
Patrick Venturea58197c2018-06-11 15:29:45 -070037 // Build a PID Zone.
38
ykchiu7c6d35d2023-05-10 17:01:46 +080039 sdbusplus::SdBusMock sdbus_mock_passive, sdbus_mock_host, sdbus_mock_mode,
40 sdbus_mock_enable;
Patrick Venturea58197c2018-06-11 15:29:45 -070041 auto bus_mock_passive = sdbusplus::get_mocked_new(&sdbus_mock_passive);
42 auto bus_mock_host = sdbusplus::get_mocked_new(&sdbus_mock_host);
43 auto bus_mock_mode = sdbusplus::get_mocked_new(&sdbus_mock_mode);
ykchiu7c6d35d2023-05-10 17:01:46 +080044 auto bus_mock_enable = sdbusplus::get_mocked_new(&sdbus_mock_enable);
Patrick Venturea58197c2018-06-11 15:29:45 -070045
46 EXPECT_CALL(sdbus_mock_host,
47 sd_bus_add_object_manager(
Patrick Ventureda4a5dd2018-08-31 09:42:48 -070048 IsNull(), _, StrEq("/xyz/openbmc_project/extsensors")))
Patrick Venturea58197c2018-06-11 15:29:45 -070049 .WillOnce(Return(0));
50
James Feist1fe08952019-05-07 09:17:16 -070051 SensorManager m(bus_mock_passive, bus_mock_host);
Patrick Venturea58197c2018-06-11 15:29:45 -070052
53 bool defer = true;
Patrick Venturee2ec0f62018-09-04 12:30:27 -070054 const char* objPath = "/path/";
Patrick Venturea58197c2018-06-11 15:29:45 -070055 int64_t zone = 1;
James Feist3484bed2019-02-25 13:28:18 -080056 double minThermalOutput = 1000.0;
ykchiu9fe3a3c2023-05-11 13:43:54 +080057 double failSafePercent = 0;
Bonnie Lo0e8fc392022-10-05 10:20:55 +080058 conf::CycleTime cycleTime;
Patrick Venturea58197c2018-06-11 15:29:45 -070059
James Feist0709e2f2020-07-08 10:59:45 -070060 double d;
Patrick Venturea58197c2018-06-11 15:29:45 -070061 std::vector<std::string> properties;
Patrick Ventureda4a5dd2018-08-31 09:42:48 -070062 SetupDbusObject(&sdbus_mock_mode, defer, objPath, modeInterface, properties,
James Feist0709e2f2020-07-08 10:59:45 -070063 &d);
Harvey Wucc0232a2023-02-09 14:58:55 +080064 SetupDbusObject(&sdbus_mock_mode, defer, objPath, debugZoneInterface,
65 properties, &d);
Patrick Venturea58197c2018-06-11 15:29:45 -070066
ykchiu7c6d35d2023-05-10 17:01:46 +080067 std::string sensorname = "temp1";
68 std::string pidsensorpath = "/xyz/openbmc_project/settings/fanctrl/zone1/" +
69 sensorname;
70
71 double de;
72 std::vector<std::string> propertiesenable;
73 SetupDbusObject(&sdbus_mock_enable, defer, pidsensorpath.c_str(),
74 enableInterface, propertiesenable, &de);
75
Bonnie Lo0e8fc392022-10-05 10:20:55 +080076 DbusPidZone p(zone, minThermalOutput, failSafePercent, cycleTime, m,
77 bus_mock_mode, objPath, defer);
Patrick Venturea58197c2018-06-11 15:29:45 -070078 // Success.
79}
80
Patrick Ventureda4a5dd2018-08-31 09:42:48 -070081} // namespace
Patrick Venturea58197c2018-06-11 15:29:45 -070082
Patrick Ventureda4a5dd2018-08-31 09:42:48 -070083class PidZoneTest : public ::testing::Test
84{
85 protected:
86 PidZoneTest() :
87 property_index(), properties(), sdbus_mock_passive(), sdbus_mock_host(),
ykchiu7c6d35d2023-05-10 17:01:46 +080088 sdbus_mock_mode(), sdbus_mock_enable()
Patrick Ventureda4a5dd2018-08-31 09:42:48 -070089 {
90 EXPECT_CALL(sdbus_mock_host,
91 sd_bus_add_object_manager(
92 IsNull(), _, StrEq("/xyz/openbmc_project/extsensors")))
93 .WillOnce(Return(0));
Patrick Venturea58197c2018-06-11 15:29:45 -070094
Patrick Ventureda4a5dd2018-08-31 09:42:48 -070095 auto bus_mock_passive = sdbusplus::get_mocked_new(&sdbus_mock_passive);
96 auto bus_mock_host = sdbusplus::get_mocked_new(&sdbus_mock_host);
97 auto bus_mock_mode = sdbusplus::get_mocked_new(&sdbus_mock_mode);
ykchiu7c6d35d2023-05-10 17:01:46 +080098 auto bus_mock_enable = sdbusplus::get_mocked_new(&sdbus_mock_enable);
Patrick Venturea58197c2018-06-11 15:29:45 -070099
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700100 // Compiler weirdly not happy about just instantiating mgr(...);
James Feist1fe08952019-05-07 09:17:16 -0700101 SensorManager m(bus_mock_passive, bus_mock_host);
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700102 mgr = std::move(m);
Patrick Venturea58197c2018-06-11 15:29:45 -0700103
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700104 SetupDbusObject(&sdbus_mock_mode, defer, objPath, modeInterface,
105 properties, &property_index);
Harvey Wucc0232a2023-02-09 14:58:55 +0800106 SetupDbusObject(&sdbus_mock_mode, defer, objPath, debugZoneInterface,
107 properties, &property_index);
Patrick Venturea58197c2018-06-11 15:29:45 -0700108
ykchiu7c6d35d2023-05-10 17:01:46 +0800109 SetupDbusObject(&sdbus_mock_enable, defer, pidsensorpath.c_str(),
110 enableInterface, propertiesenable,
111 &propertyenable_index);
112
Patrick Venture597ebd62020-08-11 08:48:19 -0700113 zone = std::make_unique<DbusPidZone>(zoneId, minThermalOutput,
Bonnie Lo0e8fc392022-10-05 10:20:55 +0800114 failSafePercent, cycleTime, mgr,
Patrick Venture597ebd62020-08-11 08:48:19 -0700115 bus_mock_mode, objPath, defer);
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700116 }
Patrick Venturea58197c2018-06-11 15:29:45 -0700117
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700118 // unused
James Feist0709e2f2020-07-08 10:59:45 -0700119 double property_index;
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700120 std::vector<std::string> properties;
ykchiu7c6d35d2023-05-10 17:01:46 +0800121 double propertyenable_index;
122 std::vector<std::string> propertiesenable;
Patrick Venturea58197c2018-06-11 15:29:45 -0700123
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700124 sdbusplus::SdBusMock sdbus_mock_passive;
125 sdbusplus::SdBusMock sdbus_mock_host;
126 sdbusplus::SdBusMock sdbus_mock_mode;
ykchiu7c6d35d2023-05-10 17:01:46 +0800127 sdbusplus::SdBusMock sdbus_mock_enable;
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700128 int64_t zoneId = 1;
James Feist3484bed2019-02-25 13:28:18 -0800129 double minThermalOutput = 1000.0;
ykchiu9fe3a3c2023-05-11 13:43:54 +0800130 double failSafePercent = 0;
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700131 bool defer = true;
Patrick Venturee2ec0f62018-09-04 12:30:27 -0700132 const char* objPath = "/path/";
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700133 SensorManager mgr;
Bonnie Lo0e8fc392022-10-05 10:20:55 +0800134 conf::CycleTime cycleTime;
Patrick Venturea58197c2018-06-11 15:29:45 -0700135
ykchiu7c6d35d2023-05-10 17:01:46 +0800136 std::string sensorname = "temp1";
137 std::string pidsensorpath = "/xyz/openbmc_project/settings/fanctrl/zone1/" +
138 sensorname;
139
Patrick Venture597ebd62020-08-11 08:48:19 -0700140 std::unique_ptr<DbusPidZone> zone;
Patrick Venturea58197c2018-06-11 15:29:45 -0700141};
142
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700143TEST_F(PidZoneTest, GetZoneId_ReturnsExpected)
144{
Patrick Venturea58197c2018-06-11 15:29:45 -0700145 // Verifies the zoneId returned is what we expect.
146
Patrick Venture0bbeaf82018-10-30 18:50:31 -0700147 EXPECT_EQ(zoneId, zone->getZoneID());
Patrick Venturea58197c2018-06-11 15:29:45 -0700148}
149
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700150TEST_F(PidZoneTest, GetAndSetManualModeTest_BehavesAsExpected)
151{
Patrick Venturea58197c2018-06-11 15:29:45 -0700152 // Verifies that the zone starts in manual mode. Verifies that one can set
153 // the mode.
154 EXPECT_FALSE(zone->getManualMode());
155
156 zone->setManualMode(true);
157 EXPECT_TRUE(zone->getManualMode());
158}
159
ykchiu7c6d35d2023-05-10 17:01:46 +0800160TEST_F(PidZoneTest, AddPidControlProcessGetAndSetEnableTest_BehavesAsExpected)
161{
162 // Verifies that the zone starts in enable mode. Verifies that one can set
163 // enable the mode.
164 auto bus_mock_enable = sdbusplus::get_mocked_new(&sdbus_mock_enable);
165
166 EXPECT_CALL(sdbus_mock_mode, sd_bus_emit_properties_changed_strv(
167 IsNull(), StrEq(pidsensorpath.c_str()),
168 StrEq(enableInterface), NotNull()))
169 .Times(::testing::AnyNumber())
170 .WillOnce(Invoke(
171 [&]([[maybe_unused]] sd_bus* bus, [[maybe_unused]] const char* path,
172 [[maybe_unused]] const char* interface, const char** names) {
173 EXPECT_STREQ("Enable", names[0]);
174 return 0;
175 }));
176
177 zone->addPidControlProcess(sensorname, bus_mock_enable,
178 pidsensorpath.c_str(), defer);
179 EXPECT_TRUE(zone->isPidProcessEnabled(sensorname));
180}
181
Josh Lehana4146eb2020-10-01 11:49:09 -0700182TEST_F(PidZoneTest, SetManualMode_RedundantWritesEnabledOnceAfterManualMode)
183{
184 // Tests adding a fan PID controller to the zone, and verifies it's
185 // touched during processing.
186
187 std::unique_ptr<PIDController> tpid =
188 std::make_unique<ControllerMock>("fan1", zone.get());
189 ControllerMock* tmock = reinterpret_cast<ControllerMock*>(tpid.get());
190
191 // Access the internal pid configuration to clear it out (unrelated to the
192 // test).
193 ec::pid_info_t* info = tpid->getPIDInfo();
194 std::memset(info, 0x00, sizeof(ec::pid_info_t));
195
196 zone->addFanPID(std::move(tpid));
197
198 EXPECT_CALL(*tmock, setptProc()).WillOnce(Return(10.0));
199 EXPECT_CALL(*tmock, inputProc()).WillOnce(Return(11.0));
200 EXPECT_CALL(*tmock, outputProc(_));
201
202 // while zone is in auto mode redundant writes should be disabled
203 EXPECT_FALSE(zone->getRedundantWrite());
204
205 // but switching from manual to auto enables a single redundant write
206 zone->setManualMode(true);
207 zone->setManualMode(false);
208 EXPECT_TRUE(zone->getRedundantWrite());
209
210 // after one iteration of a pid loop redundant write should be cleared
211 zone->processFans();
212 EXPECT_FALSE(zone->getRedundantWrite());
213}
214
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700215TEST_F(PidZoneTest, RpmSetPoints_AddMaxClear_BehaveAsExpected)
216{
Patrick Venturef7a2dd52019-07-16 14:31:13 -0700217 // Tests addSetPoint, clearSetPoints, determineMaxSetPointRequest
Nirav Shahccc8bb62022-02-17 21:06:51 -0800218 // and getMinThermalSetPoint.
Patrick Venturea58197c2018-06-11 15:29:45 -0700219
ykchiu7c6d35d2023-05-10 17:01:46 +0800220 // Need to add pid control process for the zone that can enable
221 // the process and add the set point.
222 auto bus_mock_enable = sdbusplus::get_mocked_new(&sdbus_mock_enable);
223
224 EXPECT_CALL(sdbus_mock_mode, sd_bus_emit_properties_changed_strv(
225 IsNull(), StrEq(pidsensorpath.c_str()),
226 StrEq(enableInterface), NotNull()))
227 .Times(::testing::AnyNumber())
228 .WillOnce(Invoke(
229 [&]([[maybe_unused]] sd_bus* bus, [[maybe_unused]] const char* path,
230 [[maybe_unused]] const char* interface, const char** names) {
231 EXPECT_STREQ("Enable", names[0]);
232 return 0;
233 }));
234
235 zone->addPidControlProcess(sensorname, bus_mock_enable,
236 pidsensorpath.c_str(), defer);
237
Patrick Venturea58197c2018-06-11 15:29:45 -0700238 // At least one value must be above the minimum thermal setpoint used in
239 // the constructor otherwise it'll choose that value
Patrick Venture5f59c0f2018-11-11 12:55:14 -0800240 std::vector<double> values = {100, 200, 300, 400, 500, 5000};
ykchiu7c6d35d2023-05-10 17:01:46 +0800241
Patrick Venturea58197c2018-06-11 15:29:45 -0700242 for (auto v : values)
243 {
ykchiu7c6d35d2023-05-10 17:01:46 +0800244 zone->addSetPoint(v, sensorname);
Patrick Venturea58197c2018-06-11 15:29:45 -0700245 }
246
247 // This will pull the maximum RPM setpoint request.
Patrick Venturef7a2dd52019-07-16 14:31:13 -0700248 zone->determineMaxSetPointRequest();
249 EXPECT_EQ(5000, zone->getMaxSetPointRequest());
Patrick Venturea58197c2018-06-11 15:29:45 -0700250
251 // Clear the values, so it'll choose the minimum thermal setpoint.
Patrick Venture9bbf3332019-07-16 10:50:37 -0700252 zone->clearSetPoints();
Patrick Venturea58197c2018-06-11 15:29:45 -0700253
254 // This will go through the RPM set point values and grab the maximum.
Patrick Venturef7a2dd52019-07-16 14:31:13 -0700255 zone->determineMaxSetPointRequest();
Nirav Shahccc8bb62022-02-17 21:06:51 -0800256 EXPECT_EQ(zone->getMinThermalSetPoint(), zone->getMaxSetPointRequest());
Patrick Venturea58197c2018-06-11 15:29:45 -0700257}
258
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700259TEST_F(PidZoneTest, RpmSetPoints_AddBelowMinimum_BehavesAsExpected)
260{
Patrick Venturea58197c2018-06-11 15:29:45 -0700261 // Tests adding several RPM setpoints, however, they're all lower than the
Patrick Venture7280e272019-02-11 10:45:32 -0800262 // configured minimal thermal setpoint RPM value.
Patrick Venturea58197c2018-06-11 15:29:45 -0700263
ykchiu7c6d35d2023-05-10 17:01:46 +0800264 // Need to add pid control process for the zone that can enable
265 // the process and add the set point.
266 auto bus_mock_enable = sdbusplus::get_mocked_new(&sdbus_mock_enable);
267
268 EXPECT_CALL(sdbus_mock_mode, sd_bus_emit_properties_changed_strv(
269 IsNull(), StrEq(pidsensorpath.c_str()),
270 StrEq(enableInterface), NotNull()))
271 .Times(::testing::AnyNumber())
272 .WillOnce(Invoke(
273 [&]([[maybe_unused]] sd_bus* bus, [[maybe_unused]] const char* path,
274 [[maybe_unused]] const char* interface, const char** names) {
275 EXPECT_STREQ("Enable", names[0]);
276 return 0;
277 }));
278
279 zone->addPidControlProcess(sensorname, bus_mock_enable,
280 pidsensorpath.c_str(), defer);
281
Patrick Venture5f59c0f2018-11-11 12:55:14 -0800282 std::vector<double> values = {100, 200, 300, 400, 500};
ykchiu7c6d35d2023-05-10 17:01:46 +0800283
Patrick Venturea58197c2018-06-11 15:29:45 -0700284 for (auto v : values)
285 {
ykchiu7c6d35d2023-05-10 17:01:46 +0800286 zone->addSetPoint(v, sensorname);
Patrick Venturea58197c2018-06-11 15:29:45 -0700287 }
288
289 // This will pull the maximum RPM setpoint request.
Patrick Venturef7a2dd52019-07-16 14:31:13 -0700290 zone->determineMaxSetPointRequest();
Patrick Venturea58197c2018-06-11 15:29:45 -0700291
292 // Verifies the value returned in the minimal thermal rpm set point.
Nirav Shahccc8bb62022-02-17 21:06:51 -0800293 EXPECT_EQ(zone->getMinThermalSetPoint(), zone->getMaxSetPointRequest());
Patrick Venturea58197c2018-06-11 15:29:45 -0700294}
295
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700296TEST_F(PidZoneTest, GetFailSafePercent_ReturnsExpected)
297{
Patrick Venturea58197c2018-06-11 15:29:45 -0700298 // Verify the value used to create the object is stored.
ykchiu9fe3a3c2023-05-11 13:43:54 +0800299 // when the final failsafe percent is zero , it indicate
300 // no failsafe percent is configured  , set it to 100% as
301 // the default setting.
302
303 std::vector<double> values = {0, 0, 0};
304 int64_t defaultPercent = 100;
305
306 zone->addPidFailSafePercent("temp1", values[0]);
307 zone->addPidFailSafePercent("temp2", values[1]);
308 zone->addPidFailSafePercent("temp3", values[2]);
309
310 zone->initPidFailSafePercent();
311
312 EXPECT_EQ(defaultPercent, zone->getFailSafePercent());
313}
314
315TEST_F(PidZoneTest, GetFailSafePercent_VerifyReturnsExpected)
316{
317 // Tests adding PID controller with FailSafePercent to the zone,
318 // and verifies it's returned as expected.
319
320 std::vector<double> values = {60, 80, 70};
321 double max_value = 0;
322
323 for (const auto& value : values)
324 {
325 max_value = std::max(max_value, value);
326 }
327
328 zone->addPidFailSafePercent("temp1", values[0]);
329 zone->addPidFailSafePercent("temp2", values[1]);
330 zone->addPidFailSafePercent("temp3", values[2]);
331
332 zone->initPidFailSafePercent();
333
334 EXPECT_EQ(max_value, zone->getFailSafePercent());
Patrick Venturea58197c2018-06-11 15:29:45 -0700335}
336
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700337TEST_F(PidZoneTest, ThermalInputs_FailsafeToValid_ReadsSensors)
338{
Patrick Venturea58197c2018-06-11 15:29:45 -0700339 // This test will add a couple thermal inputs, and verify that the zone
340 // initializes into failsafe mode, and will read each sensor.
341
342 std::string name1 = "temp1";
343 int64_t timeout = 1;
344
Patrick Williams8c051122023-05-10 07:50:59 -0500345 std::unique_ptr<Sensor> sensor1 = std::make_unique<SensorMock>(name1,
346 timeout);
Patrick Venturee2ec0f62018-09-04 12:30:27 -0700347 SensorMock* sensor_ptr1 = reinterpret_cast<SensorMock*>(sensor1.get());
Patrick Venturea58197c2018-06-11 15:29:45 -0700348
349 std::string name2 = "temp2";
Patrick Williams8c051122023-05-10 07:50:59 -0500350 std::unique_ptr<Sensor> sensor2 = std::make_unique<SensorMock>(name2,
351 timeout);
Patrick Venturee2ec0f62018-09-04 12:30:27 -0700352 SensorMock* sensor_ptr2 = reinterpret_cast<SensorMock*>(sensor2.get());
Patrick Venturea58197c2018-06-11 15:29:45 -0700353
354 std::string type = "unchecked";
355 mgr.addSensor(type, name1, std::move(sensor1));
356 EXPECT_EQ(mgr.getSensor(name1), sensor_ptr1);
357 mgr.addSensor(type, name2, std::move(sensor2));
358 EXPECT_EQ(mgr.getSensor(name2), sensor_ptr2);
359
360 // Now that the sensors exist, add them to the zone.
361 zone->addThermalInput(name1);
362 zone->addThermalInput(name2);
363
364 // Initialize Zone
365 zone->initializeCache();
366
367 // Verify now in failsafe mode.
368 EXPECT_TRUE(zone->getFailSafeMode());
369
370 ReadReturn r1;
371 r1.value = 10.0;
372 r1.updated = std::chrono::high_resolution_clock::now();
373 EXPECT_CALL(*sensor_ptr1, read()).WillOnce(Return(r1));
374
375 ReadReturn r2;
376 r2.value = 11.0;
377 r2.updated = std::chrono::high_resolution_clock::now();
378 EXPECT_CALL(*sensor_ptr2, read()).WillOnce(Return(r2));
379
380 // Read the sensors, this will put the values into the cache.
381 zone->updateSensors();
382
383 // We should no longer be in failsafe mode.
384 EXPECT_FALSE(zone->getFailSafeMode());
385
386 EXPECT_EQ(r1.value, zone->getCachedValue(name1));
387 EXPECT_EQ(r2.value, zone->getCachedValue(name2));
388}
389
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700390TEST_F(PidZoneTest, FanInputTest_VerifiesFanValuesCached)
391{
Patrick Venturea58197c2018-06-11 15:29:45 -0700392 // This will add a couple fan inputs, and verify the values are cached.
393
394 std::string name1 = "fan1";
395 int64_t timeout = 2;
396
Patrick Williams8c051122023-05-10 07:50:59 -0500397 std::unique_ptr<Sensor> sensor1 = std::make_unique<SensorMock>(name1,
398 timeout);
Patrick Venturee2ec0f62018-09-04 12:30:27 -0700399 SensorMock* sensor_ptr1 = reinterpret_cast<SensorMock*>(sensor1.get());
Patrick Venturea58197c2018-06-11 15:29:45 -0700400
401 std::string name2 = "fan2";
Patrick Williams8c051122023-05-10 07:50:59 -0500402 std::unique_ptr<Sensor> sensor2 = std::make_unique<SensorMock>(name2,
403 timeout);
Patrick Venturee2ec0f62018-09-04 12:30:27 -0700404 SensorMock* sensor_ptr2 = reinterpret_cast<SensorMock*>(sensor2.get());
Patrick Venturea58197c2018-06-11 15:29:45 -0700405
406 std::string type = "unchecked";
407 mgr.addSensor(type, name1, std::move(sensor1));
408 EXPECT_EQ(mgr.getSensor(name1), sensor_ptr1);
409 mgr.addSensor(type, name2, std::move(sensor2));
410 EXPECT_EQ(mgr.getSensor(name2), sensor_ptr2);
411
412 // Now that the sensors exist, add them to the zone.
413 zone->addFanInput(name1);
414 zone->addFanInput(name2);
415
416 // Initialize Zone
417 zone->initializeCache();
418
419 ReadReturn r1;
420 r1.value = 10.0;
421 r1.updated = std::chrono::high_resolution_clock::now();
422 EXPECT_CALL(*sensor_ptr1, read()).WillOnce(Return(r1));
423
424 ReadReturn r2;
425 r2.value = 11.0;
426 r2.updated = std::chrono::high_resolution_clock::now();
427 EXPECT_CALL(*sensor_ptr2, read()).WillOnce(Return(r2));
428
429 // Method under test will read through each fan sensor for the zone and
430 // cache the values.
431 zone->updateFanTelemetry();
432
433 EXPECT_EQ(r1.value, zone->getCachedValue(name1));
434 EXPECT_EQ(r2.value, zone->getCachedValue(name2));
435}
436
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700437TEST_F(PidZoneTest, ThermalInput_ValueTimeoutEntersFailSafeMode)
438{
Patrick Venturea58197c2018-06-11 15:29:45 -0700439 // On the second updateSensors call, the updated timestamp will be beyond
440 // the timeout limit.
441
442 int64_t timeout = 1;
443
444 std::string name1 = "temp1";
Patrick Williams8c051122023-05-10 07:50:59 -0500445 std::unique_ptr<Sensor> sensor1 = std::make_unique<SensorMock>(name1,
446 timeout);
Patrick Venturee2ec0f62018-09-04 12:30:27 -0700447 SensorMock* sensor_ptr1 = reinterpret_cast<SensorMock*>(sensor1.get());
Patrick Venturea58197c2018-06-11 15:29:45 -0700448
449 std::string name2 = "temp2";
Patrick Williams8c051122023-05-10 07:50:59 -0500450 std::unique_ptr<Sensor> sensor2 = std::make_unique<SensorMock>(name2,
451 timeout);
Patrick Venturee2ec0f62018-09-04 12:30:27 -0700452 SensorMock* sensor_ptr2 = reinterpret_cast<SensorMock*>(sensor2.get());
Patrick Venturea58197c2018-06-11 15:29:45 -0700453
454 std::string type = "unchecked";
455 mgr.addSensor(type, name1, std::move(sensor1));
456 EXPECT_EQ(mgr.getSensor(name1), sensor_ptr1);
457 mgr.addSensor(type, name2, std::move(sensor2));
458 EXPECT_EQ(mgr.getSensor(name2), sensor_ptr2);
459
460 zone->addThermalInput(name1);
461 zone->addThermalInput(name2);
462
463 // Initialize Zone
464 zone->initializeCache();
465
466 // Verify now in failsafe mode.
467 EXPECT_TRUE(zone->getFailSafeMode());
468
469 ReadReturn r1;
470 r1.value = 10.0;
471 r1.updated = std::chrono::high_resolution_clock::now();
472 EXPECT_CALL(*sensor_ptr1, read()).WillOnce(Return(r1));
473
474 ReadReturn r2;
475 r2.value = 11.0;
476 r2.updated = std::chrono::high_resolution_clock::now();
477 EXPECT_CALL(*sensor_ptr2, read()).WillOnce(Return(r2));
478
479 zone->updateSensors();
480 EXPECT_FALSE(zone->getFailSafeMode());
481
482 // Ok, so we're not in failsafe mode, so let's set updated to the past.
483 // sensor1 will have an updated field older than its timeout value, but
484 // sensor2 will be fine. :D
485 r1.updated -= std::chrono::seconds(3);
486 r2.updated = std::chrono::high_resolution_clock::now();
487
488 EXPECT_CALL(*sensor_ptr1, read()).WillOnce(Return(r1));
489 EXPECT_CALL(*sensor_ptr2, read()).WillOnce(Return(r2));
490
491 // Method under test will read each sensor. One sensor's value is older
492 // than the timeout for that sensor and this triggers failsafe mode.
493 zone->updateSensors();
494 EXPECT_TRUE(zone->getFailSafeMode());
495}
496
Will Liangded0ab52019-05-15 17:10:06 +0800497TEST_F(PidZoneTest, FanInputTest_FailsafeToValid_ReadsSensors)
498{
499 // This will add a couple fan inputs, and verify the values are cached.
500
501 std::string name1 = "fan1";
502 int64_t timeout = 2;
503
Patrick Williams8c051122023-05-10 07:50:59 -0500504 std::unique_ptr<Sensor> sensor1 = std::make_unique<SensorMock>(name1,
505 timeout);
Will Liangded0ab52019-05-15 17:10:06 +0800506 SensorMock* sensor_ptr1 = reinterpret_cast<SensorMock*>(sensor1.get());
507
508 std::string name2 = "fan2";
Patrick Williams8c051122023-05-10 07:50:59 -0500509 std::unique_ptr<Sensor> sensor2 = std::make_unique<SensorMock>(name2,
510 timeout);
Will Liangded0ab52019-05-15 17:10:06 +0800511 SensorMock* sensor_ptr2 = reinterpret_cast<SensorMock*>(sensor2.get());
512
513 std::string type = "unchecked";
514 mgr.addSensor(type, name1, std::move(sensor1));
515 EXPECT_EQ(mgr.getSensor(name1), sensor_ptr1);
516 mgr.addSensor(type, name2, std::move(sensor2));
517 EXPECT_EQ(mgr.getSensor(name2), sensor_ptr2);
518
519 // Now that the sensors exist, add them to the zone.
520 zone->addFanInput(name1);
521 zone->addFanInput(name2);
522
523 // Initialize Zone
524 zone->initializeCache();
525
526 // Verify now in failsafe mode.
527 EXPECT_TRUE(zone->getFailSafeMode());
528
529 ReadReturn r1;
530 r1.value = 10.0;
531 r1.updated = std::chrono::high_resolution_clock::now();
532 EXPECT_CALL(*sensor_ptr1, read()).WillOnce(Return(r1));
533
534 ReadReturn r2;
535 r2.value = 11.0;
536 r2.updated = std::chrono::high_resolution_clock::now();
537 EXPECT_CALL(*sensor_ptr2, read()).WillOnce(Return(r2));
538
539 // Method under test will read through each fan sensor for the zone and
540 // cache the values.
541 zone->updateFanTelemetry();
542
543 // We should no longer be in failsafe mode.
544 EXPECT_FALSE(zone->getFailSafeMode());
545
546 EXPECT_EQ(r1.value, zone->getCachedValue(name1));
547 EXPECT_EQ(r2.value, zone->getCachedValue(name2));
548}
549
550TEST_F(PidZoneTest, FanInputTest_ValueTimeoutEntersFailSafeMode)
551{
552 // This will add a couple fan inputs, and verify the values are cached.
553
554 std::string name1 = "fan1";
555 int64_t timeout = 2;
556
Patrick Williams8c051122023-05-10 07:50:59 -0500557 std::unique_ptr<Sensor> sensor1 = std::make_unique<SensorMock>(name1,
558 timeout);
Will Liangded0ab52019-05-15 17:10:06 +0800559 SensorMock* sensor_ptr1 = reinterpret_cast<SensorMock*>(sensor1.get());
560
561 std::string name2 = "fan2";
Patrick Williams8c051122023-05-10 07:50:59 -0500562 std::unique_ptr<Sensor> sensor2 = std::make_unique<SensorMock>(name2,
563 timeout);
Will Liangded0ab52019-05-15 17:10:06 +0800564 SensorMock* sensor_ptr2 = reinterpret_cast<SensorMock*>(sensor2.get());
565
566 std::string type = "unchecked";
567 mgr.addSensor(type, name1, std::move(sensor1));
568 EXPECT_EQ(mgr.getSensor(name1), sensor_ptr1);
569 mgr.addSensor(type, name2, std::move(sensor2));
570 EXPECT_EQ(mgr.getSensor(name2), sensor_ptr2);
571
572 // Now that the sensors exist, add them to the zone.
573 zone->addFanInput(name1);
574 zone->addFanInput(name2);
575
576 // Initialize Zone
577 zone->initializeCache();
578
579 // Verify now in failsafe mode.
580 EXPECT_TRUE(zone->getFailSafeMode());
581
582 ReadReturn r1;
583 r1.value = 10.0;
584 r1.updated = std::chrono::high_resolution_clock::now();
585 EXPECT_CALL(*sensor_ptr1, read()).WillOnce(Return(r1));
586
587 ReadReturn r2;
588 r2.value = 11.0;
589 r2.updated = std::chrono::high_resolution_clock::now();
590 EXPECT_CALL(*sensor_ptr2, read()).WillOnce(Return(r2));
591
592 // Method under test will read through each fan sensor for the zone and
593 // cache the values.
594 zone->updateFanTelemetry();
595
596 // We should no longer be in failsafe mode.
597 EXPECT_FALSE(zone->getFailSafeMode());
598
599 r1.updated -= std::chrono::seconds(3);
600 r2.updated = std::chrono::high_resolution_clock::now();
601
602 EXPECT_CALL(*sensor_ptr1, read()).WillOnce(Return(r1));
603 EXPECT_CALL(*sensor_ptr2, read()).WillOnce(Return(r2));
604
605 zone->updateFanTelemetry();
606 EXPECT_TRUE(zone->getFailSafeMode());
607}
608
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700609TEST_F(PidZoneTest, GetSensorTest_ReturnsExpected)
610{
Patrick Venturea58197c2018-06-11 15:29:45 -0700611 // One can grab a sensor from the manager through the zone.
612
613 int64_t timeout = 1;
614
615 std::string name1 = "temp1";
Patrick Williams8c051122023-05-10 07:50:59 -0500616 std::unique_ptr<Sensor> sensor1 = std::make_unique<SensorMock>(name1,
617 timeout);
Patrick Venturee2ec0f62018-09-04 12:30:27 -0700618 SensorMock* sensor_ptr1 = reinterpret_cast<SensorMock*>(sensor1.get());
Patrick Venturea58197c2018-06-11 15:29:45 -0700619
620 std::string type = "unchecked";
621 mgr.addSensor(type, name1, std::move(sensor1));
622 EXPECT_EQ(mgr.getSensor(name1), sensor_ptr1);
623
624 zone->addThermalInput(name1);
625
626 // Verify method under test returns the pointer we expect.
627 EXPECT_EQ(mgr.getSensor(name1), zone->getSensor(name1));
628}
629
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700630TEST_F(PidZoneTest, AddThermalPIDTest_VerifiesThermalPIDsProcessed)
631{
Patrick Venturea58197c2018-06-11 15:29:45 -0700632 // Tests adding a thermal PID controller to the zone, and verifies it's
633 // touched during processing.
634
635 std::unique_ptr<PIDController> tpid =
636 std::make_unique<ControllerMock>("thermal1", zone.get());
Patrick Venturee2ec0f62018-09-04 12:30:27 -0700637 ControllerMock* tmock = reinterpret_cast<ControllerMock*>(tpid.get());
Patrick Venturea58197c2018-06-11 15:29:45 -0700638
639 // Access the internal pid configuration to clear it out (unrelated to the
640 // test).
Patrick Venture563a3562018-10-30 09:31:26 -0700641 ec::pid_info_t* info = tpid->getPIDInfo();
Patrick Venturea58197c2018-06-11 15:29:45 -0700642 std::memset(info, 0x00, sizeof(ec::pid_info_t));
643
644 zone->addThermalPID(std::move(tpid));
645
Patrick Venture563a3562018-10-30 09:31:26 -0700646 EXPECT_CALL(*tmock, setptProc()).WillOnce(Return(10.0));
647 EXPECT_CALL(*tmock, inputProc()).WillOnce(Return(11.0));
648 EXPECT_CALL(*tmock, outputProc(_));
Patrick Venturea58197c2018-06-11 15:29:45 -0700649
650 // Method under test will, for each thermal PID, call setpt, input, and
651 // output.
Patrick Venture563a3562018-10-30 09:31:26 -0700652 zone->processThermals();
Patrick Venturea58197c2018-06-11 15:29:45 -0700653}
654
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700655TEST_F(PidZoneTest, AddFanPIDTest_VerifiesFanPIDsProcessed)
656{
Patrick Venturea58197c2018-06-11 15:29:45 -0700657 // Tests adding a fan PID controller to the zone, and verifies it's
658 // touched during processing.
659
660 std::unique_ptr<PIDController> tpid =
661 std::make_unique<ControllerMock>("fan1", zone.get());
Patrick Venturee2ec0f62018-09-04 12:30:27 -0700662 ControllerMock* tmock = reinterpret_cast<ControllerMock*>(tpid.get());
Patrick Venturea58197c2018-06-11 15:29:45 -0700663
664 // Access the internal pid configuration to clear it out (unrelated to the
665 // test).
Patrick Venture563a3562018-10-30 09:31:26 -0700666 ec::pid_info_t* info = tpid->getPIDInfo();
Patrick Venturea58197c2018-06-11 15:29:45 -0700667 std::memset(info, 0x00, sizeof(ec::pid_info_t));
668
669 zone->addFanPID(std::move(tpid));
670
Patrick Venture563a3562018-10-30 09:31:26 -0700671 EXPECT_CALL(*tmock, setptProc()).WillOnce(Return(10.0));
672 EXPECT_CALL(*tmock, inputProc()).WillOnce(Return(11.0));
673 EXPECT_CALL(*tmock, outputProc(_));
Patrick Venturea58197c2018-06-11 15:29:45 -0700674
675 // Method under test will, for each fan PID, call setpt, input, and output.
Patrick Venture563a3562018-10-30 09:31:26 -0700676 zone->processFans();
Patrick Venturea58197c2018-06-11 15:29:45 -0700677}
678
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700679TEST_F(PidZoneTest, ManualModeDbusTest_VerifySetManualBehavesAsExpected)
680{
Patrick Venturea58197c2018-06-11 15:29:45 -0700681 // The manual(bool) method is inherited from the dbus mode interface.
682
683 // Verifies that someone doesn't remove the internal call to the dbus
684 // object from which we're inheriting.
685 EXPECT_CALL(sdbus_mock_mode,
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700686 sd_bus_emit_properties_changed_strv(
687 IsNull(), StrEq(objPath), StrEq(modeInterface), NotNull()))
Harvey.Wua1ae4fa2022-10-28 17:38:35 +0800688 .WillOnce(Invoke(
689 [&]([[maybe_unused]] sd_bus* bus, [[maybe_unused]] const char* path,
690 [[maybe_unused]] const char* interface, const char** names) {
Patrick Williams8c051122023-05-10 07:50:59 -0500691 EXPECT_STREQ("Manual", names[0]);
692 return 0;
693 }));
Patrick Venturea58197c2018-06-11 15:29:45 -0700694
695 // Method under test will set the manual mode to true and broadcast this
696 // change on dbus.
697 zone->manual(true);
698 EXPECT_TRUE(zone->getManualMode());
699}
700
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700701TEST_F(PidZoneTest, FailsafeDbusTest_VerifiesReturnsExpected)
702{
Patrick Venturea58197c2018-06-11 15:29:45 -0700703 // This property is implemented by us as read-only, such that trying to
704 // write to it will have no effect.
705 EXPECT_EQ(zone->failSafe(), zone->getFailSafeMode());
706}
Patrick Venturea0764872020-08-08 07:48:43 -0700707
708} // namespace
709} // namespace pid_control