blob: f0b96b15b41dd39e1a400fba11fb287ada689c08 [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";
Harvey Wu37180062023-10-02 09:42:50 +080031static std::string debugThermalPowerInterface =
32 "xyz.openbmc_project.Debug.Pid.ThermalPower";
Patrick Venturea58197c2018-06-11 15:29:45 -070033
Patrick Ventureda4a5dd2018-08-31 09:42:48 -070034namespace
35{
Patrick Venturea58197c2018-06-11 15:29:45 -070036
Patrick Ventureda4a5dd2018-08-31 09:42:48 -070037TEST(PidZoneConstructorTest, BoringConstructorTest)
38{
Patrick Venturea58197c2018-06-11 15:29:45 -070039 // Build a PID Zone.
40
ykchiu7c6d35d2023-05-10 17:01:46 +080041 sdbusplus::SdBusMock sdbus_mock_passive, sdbus_mock_host, sdbus_mock_mode,
42 sdbus_mock_enable;
Patrick Venturea58197c2018-06-11 15:29:45 -070043 auto bus_mock_passive = sdbusplus::get_mocked_new(&sdbus_mock_passive);
44 auto bus_mock_host = sdbusplus::get_mocked_new(&sdbus_mock_host);
45 auto bus_mock_mode = sdbusplus::get_mocked_new(&sdbus_mock_mode);
ykchiu7c6d35d2023-05-10 17:01:46 +080046 auto bus_mock_enable = sdbusplus::get_mocked_new(&sdbus_mock_enable);
Patrick Venturea58197c2018-06-11 15:29:45 -070047
48 EXPECT_CALL(sdbus_mock_host,
49 sd_bus_add_object_manager(
Patrick Ventureda4a5dd2018-08-31 09:42:48 -070050 IsNull(), _, StrEq("/xyz/openbmc_project/extsensors")))
Patrick Venturea58197c2018-06-11 15:29:45 -070051 .WillOnce(Return(0));
52
James Feist1fe08952019-05-07 09:17:16 -070053 SensorManager m(bus_mock_passive, bus_mock_host);
Patrick Venturea58197c2018-06-11 15:29:45 -070054
55 bool defer = true;
Patrick Venturee2ec0f62018-09-04 12:30:27 -070056 const char* objPath = "/path/";
Patrick Venturea58197c2018-06-11 15:29:45 -070057 int64_t zone = 1;
James Feist3484bed2019-02-25 13:28:18 -080058 double minThermalOutput = 1000.0;
ykchiu9fe3a3c2023-05-11 13:43:54 +080059 double failSafePercent = 0;
Bonnie Lo0e8fc392022-10-05 10:20:55 +080060 conf::CycleTime cycleTime;
Patrick Venturea58197c2018-06-11 15:29:45 -070061
James Feist0709e2f2020-07-08 10:59:45 -070062 double d;
Patrick Venturea58197c2018-06-11 15:29:45 -070063 std::vector<std::string> properties;
Patrick Ventureda4a5dd2018-08-31 09:42:48 -070064 SetupDbusObject(&sdbus_mock_mode, defer, objPath, modeInterface, properties,
James Feist0709e2f2020-07-08 10:59:45 -070065 &d);
Harvey Wucc0232a2023-02-09 14:58:55 +080066 SetupDbusObject(&sdbus_mock_mode, defer, objPath, debugZoneInterface,
67 properties, &d);
Patrick Venturea58197c2018-06-11 15:29:45 -070068
ykchiu7c6d35d2023-05-10 17:01:46 +080069 std::string sensorname = "temp1";
70 std::string pidsensorpath = "/xyz/openbmc_project/settings/fanctrl/zone1/" +
71 sensorname;
72
73 double de;
74 std::vector<std::string> propertiesenable;
75 SetupDbusObject(&sdbus_mock_enable, defer, pidsensorpath.c_str(),
76 enableInterface, propertiesenable, &de);
77
Harvey Wu37180062023-10-02 09:42:50 +080078 EXPECT_CALL(sdbus_mock_enable,
79 sd_bus_add_object_vtable(
80 IsNull(), NotNull(), StrEq(pidsensorpath.c_str()),
81 StrEq(debugThermalPowerInterface), NotNull(), NotNull()))
82 .Times(::testing::AnyNumber())
83 .WillOnce(Return(0));
84
Bonnie Lo0e8fc392022-10-05 10:20:55 +080085 DbusPidZone p(zone, minThermalOutput, failSafePercent, cycleTime, m,
86 bus_mock_mode, objPath, defer);
Patrick Venturea58197c2018-06-11 15:29:45 -070087 // Success.
88}
89
Patrick Ventureda4a5dd2018-08-31 09:42:48 -070090} // namespace
Patrick Venturea58197c2018-06-11 15:29:45 -070091
Patrick Ventureda4a5dd2018-08-31 09:42:48 -070092class PidZoneTest : public ::testing::Test
93{
94 protected:
95 PidZoneTest() :
96 property_index(), properties(), sdbus_mock_passive(), sdbus_mock_host(),
ykchiu7c6d35d2023-05-10 17:01:46 +080097 sdbus_mock_mode(), sdbus_mock_enable()
Patrick Ventureda4a5dd2018-08-31 09:42:48 -070098 {
99 EXPECT_CALL(sdbus_mock_host,
100 sd_bus_add_object_manager(
101 IsNull(), _, StrEq("/xyz/openbmc_project/extsensors")))
102 .WillOnce(Return(0));
Patrick Venturea58197c2018-06-11 15:29:45 -0700103
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700104 auto bus_mock_passive = sdbusplus::get_mocked_new(&sdbus_mock_passive);
105 auto bus_mock_host = sdbusplus::get_mocked_new(&sdbus_mock_host);
106 auto bus_mock_mode = sdbusplus::get_mocked_new(&sdbus_mock_mode);
ykchiu7c6d35d2023-05-10 17:01:46 +0800107 auto bus_mock_enable = sdbusplus::get_mocked_new(&sdbus_mock_enable);
Patrick Venturea58197c2018-06-11 15:29:45 -0700108
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700109 // Compiler weirdly not happy about just instantiating mgr(...);
James Feist1fe08952019-05-07 09:17:16 -0700110 SensorManager m(bus_mock_passive, bus_mock_host);
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700111 mgr = std::move(m);
Patrick Venturea58197c2018-06-11 15:29:45 -0700112
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700113 SetupDbusObject(&sdbus_mock_mode, defer, objPath, modeInterface,
114 properties, &property_index);
Harvey Wucc0232a2023-02-09 14:58:55 +0800115 SetupDbusObject(&sdbus_mock_mode, defer, objPath, debugZoneInterface,
116 properties, &property_index);
Patrick Venturea58197c2018-06-11 15:29:45 -0700117
ykchiu7c6d35d2023-05-10 17:01:46 +0800118 SetupDbusObject(&sdbus_mock_enable, defer, pidsensorpath.c_str(),
119 enableInterface, propertiesenable,
120 &propertyenable_index);
Harvey Wu37180062023-10-02 09:42:50 +0800121 EXPECT_CALL(sdbus_mock_enable,
122 sd_bus_add_object_vtable(IsNull(), NotNull(),
123 StrEq(pidsensorpath.c_str()),
124 StrEq(debugThermalPowerInterface),
125 NotNull(), NotNull()))
126 .Times(::testing::AnyNumber())
127 .WillOnce(Return(0));
ykchiu7c6d35d2023-05-10 17:01:46 +0800128
Patrick Venture597ebd62020-08-11 08:48:19 -0700129 zone = std::make_unique<DbusPidZone>(zoneId, minThermalOutput,
Bonnie Lo0e8fc392022-10-05 10:20:55 +0800130 failSafePercent, cycleTime, mgr,
Patrick Venture597ebd62020-08-11 08:48:19 -0700131 bus_mock_mode, objPath, defer);
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700132 }
Patrick Venturea58197c2018-06-11 15:29:45 -0700133
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700134 // unused
James Feist0709e2f2020-07-08 10:59:45 -0700135 double property_index;
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700136 std::vector<std::string> properties;
ykchiu7c6d35d2023-05-10 17:01:46 +0800137 double propertyenable_index;
138 std::vector<std::string> propertiesenable;
Patrick Venturea58197c2018-06-11 15:29:45 -0700139
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700140 sdbusplus::SdBusMock sdbus_mock_passive;
141 sdbusplus::SdBusMock sdbus_mock_host;
142 sdbusplus::SdBusMock sdbus_mock_mode;
ykchiu7c6d35d2023-05-10 17:01:46 +0800143 sdbusplus::SdBusMock sdbus_mock_enable;
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700144 int64_t zoneId = 1;
James Feist3484bed2019-02-25 13:28:18 -0800145 double minThermalOutput = 1000.0;
ykchiu9fe3a3c2023-05-11 13:43:54 +0800146 double failSafePercent = 0;
Harvey Wu37180062023-10-02 09:42:50 +0800147 double setpoint = 50.0;
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700148 bool defer = true;
Patrick Venturee2ec0f62018-09-04 12:30:27 -0700149 const char* objPath = "/path/";
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700150 SensorManager mgr;
Bonnie Lo0e8fc392022-10-05 10:20:55 +0800151 conf::CycleTime cycleTime;
Patrick Venturea58197c2018-06-11 15:29:45 -0700152
ykchiu7c6d35d2023-05-10 17:01:46 +0800153 std::string sensorname = "temp1";
Harvey Wu37180062023-10-02 09:42:50 +0800154 std::string sensorType = "temp";
ykchiu7c6d35d2023-05-10 17:01:46 +0800155 std::string pidsensorpath = "/xyz/openbmc_project/settings/fanctrl/zone1/" +
156 sensorname;
157
Patrick Venture597ebd62020-08-11 08:48:19 -0700158 std::unique_ptr<DbusPidZone> zone;
Patrick Venturea58197c2018-06-11 15:29:45 -0700159};
160
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700161TEST_F(PidZoneTest, GetZoneId_ReturnsExpected)
162{
Patrick Venturea58197c2018-06-11 15:29:45 -0700163 // Verifies the zoneId returned is what we expect.
164
Patrick Venture0bbeaf82018-10-30 18:50:31 -0700165 EXPECT_EQ(zoneId, zone->getZoneID());
Patrick Venturea58197c2018-06-11 15:29:45 -0700166}
167
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700168TEST_F(PidZoneTest, GetAndSetManualModeTest_BehavesAsExpected)
169{
Patrick Venturea58197c2018-06-11 15:29:45 -0700170 // Verifies that the zone starts in manual mode. Verifies that one can set
171 // the mode.
172 EXPECT_FALSE(zone->getManualMode());
173
174 zone->setManualMode(true);
175 EXPECT_TRUE(zone->getManualMode());
176}
177
ykchiu7c6d35d2023-05-10 17:01:46 +0800178TEST_F(PidZoneTest, AddPidControlProcessGetAndSetEnableTest_BehavesAsExpected)
179{
180 // Verifies that the zone starts in enable mode. Verifies that one can set
181 // enable the mode.
182 auto bus_mock_enable = sdbusplus::get_mocked_new(&sdbus_mock_enable);
183
184 EXPECT_CALL(sdbus_mock_mode, sd_bus_emit_properties_changed_strv(
185 IsNull(), StrEq(pidsensorpath.c_str()),
186 StrEq(enableInterface), NotNull()))
187 .Times(::testing::AnyNumber())
188 .WillOnce(Invoke(
189 [&]([[maybe_unused]] sd_bus* bus, [[maybe_unused]] const char* path,
190 [[maybe_unused]] const char* interface, const char** names) {
191 EXPECT_STREQ("Enable", names[0]);
192 return 0;
Patrick Williamse1dbb592023-10-20 11:19:22 -0500193 }));
ykchiu7c6d35d2023-05-10 17:01:46 +0800194
Harvey Wu37180062023-10-02 09:42:50 +0800195 zone->addPidControlProcess(sensorname, sensorType, setpoint,
196 bus_mock_enable, pidsensorpath.c_str(), defer);
ykchiu7c6d35d2023-05-10 17:01:46 +0800197 EXPECT_TRUE(zone->isPidProcessEnabled(sensorname));
198}
199
Josh Lehana4146eb2020-10-01 11:49:09 -0700200TEST_F(PidZoneTest, SetManualMode_RedundantWritesEnabledOnceAfterManualMode)
201{
202 // Tests adding a fan PID controller to the zone, and verifies it's
203 // touched during processing.
204
205 std::unique_ptr<PIDController> tpid =
206 std::make_unique<ControllerMock>("fan1", zone.get());
207 ControllerMock* tmock = reinterpret_cast<ControllerMock*>(tpid.get());
208
209 // Access the internal pid configuration to clear it out (unrelated to the
210 // test).
211 ec::pid_info_t* info = tpid->getPIDInfo();
212 std::memset(info, 0x00, sizeof(ec::pid_info_t));
213
214 zone->addFanPID(std::move(tpid));
215
216 EXPECT_CALL(*tmock, setptProc()).WillOnce(Return(10.0));
217 EXPECT_CALL(*tmock, inputProc()).WillOnce(Return(11.0));
218 EXPECT_CALL(*tmock, outputProc(_));
219
220 // while zone is in auto mode redundant writes should be disabled
221 EXPECT_FALSE(zone->getRedundantWrite());
222
223 // but switching from manual to auto enables a single redundant write
224 zone->setManualMode(true);
225 zone->setManualMode(false);
226 EXPECT_TRUE(zone->getRedundantWrite());
227
228 // after one iteration of a pid loop redundant write should be cleared
229 zone->processFans();
230 EXPECT_FALSE(zone->getRedundantWrite());
231}
232
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700233TEST_F(PidZoneTest, RpmSetPoints_AddMaxClear_BehaveAsExpected)
234{
Patrick Venturef7a2dd52019-07-16 14:31:13 -0700235 // Tests addSetPoint, clearSetPoints, determineMaxSetPointRequest
Nirav Shahccc8bb62022-02-17 21:06:51 -0800236 // and getMinThermalSetPoint.
Patrick Venturea58197c2018-06-11 15:29:45 -0700237
ykchiu7c6d35d2023-05-10 17:01:46 +0800238 // Need to add pid control process for the zone that can enable
239 // the process and add the set point.
240 auto bus_mock_enable = sdbusplus::get_mocked_new(&sdbus_mock_enable);
241
242 EXPECT_CALL(sdbus_mock_mode, sd_bus_emit_properties_changed_strv(
243 IsNull(), StrEq(pidsensorpath.c_str()),
244 StrEq(enableInterface), NotNull()))
245 .Times(::testing::AnyNumber())
246 .WillOnce(Invoke(
247 [&]([[maybe_unused]] sd_bus* bus, [[maybe_unused]] const char* path,
248 [[maybe_unused]] const char* interface, const char** names) {
249 EXPECT_STREQ("Enable", names[0]);
250 return 0;
Patrick Williamse1dbb592023-10-20 11:19:22 -0500251 }));
ykchiu7c6d35d2023-05-10 17:01:46 +0800252
Harvey Wu37180062023-10-02 09:42:50 +0800253 zone->addPidControlProcess(sensorname, sensorType, setpoint,
254 bus_mock_enable, pidsensorpath.c_str(), defer);
ykchiu7c6d35d2023-05-10 17:01:46 +0800255
Patrick Venturea58197c2018-06-11 15:29:45 -0700256 // At least one value must be above the minimum thermal setpoint used in
257 // the constructor otherwise it'll choose that value
Patrick Venture5f59c0f2018-11-11 12:55:14 -0800258 std::vector<double> values = {100, 200, 300, 400, 500, 5000};
ykchiu7c6d35d2023-05-10 17:01:46 +0800259
Patrick Venturea58197c2018-06-11 15:29:45 -0700260 for (auto v : values)
261 {
ykchiu7c6d35d2023-05-10 17:01:46 +0800262 zone->addSetPoint(v, sensorname);
Patrick Venturea58197c2018-06-11 15:29:45 -0700263 }
264
265 // This will pull the maximum RPM setpoint request.
Patrick Venturef7a2dd52019-07-16 14:31:13 -0700266 zone->determineMaxSetPointRequest();
267 EXPECT_EQ(5000, zone->getMaxSetPointRequest());
Patrick Venturea58197c2018-06-11 15:29:45 -0700268
269 // Clear the values, so it'll choose the minimum thermal setpoint.
Patrick Venture9bbf3332019-07-16 10:50:37 -0700270 zone->clearSetPoints();
Patrick Venturea58197c2018-06-11 15:29:45 -0700271
272 // This will go through the RPM set point values and grab the maximum.
Patrick Venturef7a2dd52019-07-16 14:31:13 -0700273 zone->determineMaxSetPointRequest();
Nirav Shahccc8bb62022-02-17 21:06:51 -0800274 EXPECT_EQ(zone->getMinThermalSetPoint(), zone->getMaxSetPointRequest());
Patrick Venturea58197c2018-06-11 15:29:45 -0700275}
276
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700277TEST_F(PidZoneTest, RpmSetPoints_AddBelowMinimum_BehavesAsExpected)
278{
Patrick Venturea58197c2018-06-11 15:29:45 -0700279 // Tests adding several RPM setpoints, however, they're all lower than the
Patrick Venture7280e272019-02-11 10:45:32 -0800280 // configured minimal thermal setpoint RPM value.
Patrick Venturea58197c2018-06-11 15:29:45 -0700281
ykchiu7c6d35d2023-05-10 17:01:46 +0800282 // Need to add pid control process for the zone that can enable
283 // the process and add the set point.
284 auto bus_mock_enable = sdbusplus::get_mocked_new(&sdbus_mock_enable);
285
286 EXPECT_CALL(sdbus_mock_mode, sd_bus_emit_properties_changed_strv(
287 IsNull(), StrEq(pidsensorpath.c_str()),
288 StrEq(enableInterface), NotNull()))
289 .Times(::testing::AnyNumber())
290 .WillOnce(Invoke(
291 [&]([[maybe_unused]] sd_bus* bus, [[maybe_unused]] const char* path,
292 [[maybe_unused]] const char* interface, const char** names) {
293 EXPECT_STREQ("Enable", names[0]);
294 return 0;
Patrick Williamse1dbb592023-10-20 11:19:22 -0500295 }));
ykchiu7c6d35d2023-05-10 17:01:46 +0800296
Harvey Wu37180062023-10-02 09:42:50 +0800297 zone->addPidControlProcess(sensorname, sensorType, setpoint,
298 bus_mock_enable, pidsensorpath.c_str(), defer);
ykchiu7c6d35d2023-05-10 17:01:46 +0800299
Patrick Venture5f59c0f2018-11-11 12:55:14 -0800300 std::vector<double> values = {100, 200, 300, 400, 500};
ykchiu7c6d35d2023-05-10 17:01:46 +0800301
Patrick Venturea58197c2018-06-11 15:29:45 -0700302 for (auto v : values)
303 {
ykchiu7c6d35d2023-05-10 17:01:46 +0800304 zone->addSetPoint(v, sensorname);
Patrick Venturea58197c2018-06-11 15:29:45 -0700305 }
306
307 // This will pull the maximum RPM setpoint request.
Patrick Venturef7a2dd52019-07-16 14:31:13 -0700308 zone->determineMaxSetPointRequest();
Patrick Venturea58197c2018-06-11 15:29:45 -0700309
310 // Verifies the value returned in the minimal thermal rpm set point.
Nirav Shahccc8bb62022-02-17 21:06:51 -0800311 EXPECT_EQ(zone->getMinThermalSetPoint(), zone->getMaxSetPointRequest());
Patrick Venturea58197c2018-06-11 15:29:45 -0700312}
313
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700314TEST_F(PidZoneTest, GetFailSafePercent_ReturnsExpected)
315{
Patrick Venturea58197c2018-06-11 15:29:45 -0700316 // Verify the value used to create the object is stored.
ykchiu9fe3a3c2023-05-11 13:43:54 +0800317 // when the final failsafe percent is zero , it indicate
318 // no failsafe percent is configured  , set it to 100% as
319 // the default setting.
320
321 std::vector<double> values = {0, 0, 0};
322 int64_t defaultPercent = 100;
323
324 zone->addPidFailSafePercent("temp1", values[0]);
325 zone->addPidFailSafePercent("temp2", values[1]);
326 zone->addPidFailSafePercent("temp3", values[2]);
327
328 zone->initPidFailSafePercent();
329
330 EXPECT_EQ(defaultPercent, zone->getFailSafePercent());
331}
332
333TEST_F(PidZoneTest, GetFailSafePercent_VerifyReturnsExpected)
334{
335 // Tests adding PID controller with FailSafePercent to the zone,
336 // and verifies it's returned as expected.
337
338 std::vector<double> values = {60, 80, 70};
339 double max_value = 0;
340
341 for (const auto& value : values)
342 {
343 max_value = std::max(max_value, value);
344 }
345
346 zone->addPidFailSafePercent("temp1", values[0]);
347 zone->addPidFailSafePercent("temp2", values[1]);
348 zone->addPidFailSafePercent("temp3", values[2]);
349
350 zone->initPidFailSafePercent();
351
352 EXPECT_EQ(max_value, zone->getFailSafePercent());
Patrick Venturea58197c2018-06-11 15:29:45 -0700353}
354
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700355TEST_F(PidZoneTest, ThermalInputs_FailsafeToValid_ReadsSensors)
356{
Patrick Venturea58197c2018-06-11 15:29:45 -0700357 // This test will add a couple thermal inputs, and verify that the zone
358 // initializes into failsafe mode, and will read each sensor.
359
360 std::string name1 = "temp1";
361 int64_t timeout = 1;
362
Patrick Williams8c051122023-05-10 07:50:59 -0500363 std::unique_ptr<Sensor> sensor1 = std::make_unique<SensorMock>(name1,
364 timeout);
Patrick Venturee2ec0f62018-09-04 12:30:27 -0700365 SensorMock* sensor_ptr1 = reinterpret_cast<SensorMock*>(sensor1.get());
Patrick Venturea58197c2018-06-11 15:29:45 -0700366
367 std::string name2 = "temp2";
Patrick Williams8c051122023-05-10 07:50:59 -0500368 std::unique_ptr<Sensor> sensor2 = std::make_unique<SensorMock>(name2,
369 timeout);
Patrick Venturee2ec0f62018-09-04 12:30:27 -0700370 SensorMock* sensor_ptr2 = reinterpret_cast<SensorMock*>(sensor2.get());
Patrick Venturea58197c2018-06-11 15:29:45 -0700371
372 std::string type = "unchecked";
373 mgr.addSensor(type, name1, std::move(sensor1));
374 EXPECT_EQ(mgr.getSensor(name1), sensor_ptr1);
375 mgr.addSensor(type, name2, std::move(sensor2));
376 EXPECT_EQ(mgr.getSensor(name2), sensor_ptr2);
377
378 // Now that the sensors exist, add them to the zone.
379 zone->addThermalInput(name1);
380 zone->addThermalInput(name2);
381
382 // Initialize Zone
383 zone->initializeCache();
384
385 // Verify now in failsafe mode.
386 EXPECT_TRUE(zone->getFailSafeMode());
387
388 ReadReturn r1;
389 r1.value = 10.0;
390 r1.updated = std::chrono::high_resolution_clock::now();
391 EXPECT_CALL(*sensor_ptr1, read()).WillOnce(Return(r1));
392
393 ReadReturn r2;
394 r2.value = 11.0;
395 r2.updated = std::chrono::high_resolution_clock::now();
396 EXPECT_CALL(*sensor_ptr2, read()).WillOnce(Return(r2));
397
398 // Read the sensors, this will put the values into the cache.
399 zone->updateSensors();
400
401 // We should no longer be in failsafe mode.
402 EXPECT_FALSE(zone->getFailSafeMode());
403
404 EXPECT_EQ(r1.value, zone->getCachedValue(name1));
405 EXPECT_EQ(r2.value, zone->getCachedValue(name2));
406}
407
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700408TEST_F(PidZoneTest, FanInputTest_VerifiesFanValuesCached)
409{
Patrick Venturea58197c2018-06-11 15:29:45 -0700410 // This will add a couple fan inputs, and verify the values are cached.
411
412 std::string name1 = "fan1";
413 int64_t timeout = 2;
414
Patrick Williams8c051122023-05-10 07:50:59 -0500415 std::unique_ptr<Sensor> sensor1 = std::make_unique<SensorMock>(name1,
416 timeout);
Patrick Venturee2ec0f62018-09-04 12:30:27 -0700417 SensorMock* sensor_ptr1 = reinterpret_cast<SensorMock*>(sensor1.get());
Patrick Venturea58197c2018-06-11 15:29:45 -0700418
419 std::string name2 = "fan2";
Patrick Williams8c051122023-05-10 07:50:59 -0500420 std::unique_ptr<Sensor> sensor2 = std::make_unique<SensorMock>(name2,
421 timeout);
Patrick Venturee2ec0f62018-09-04 12:30:27 -0700422 SensorMock* sensor_ptr2 = reinterpret_cast<SensorMock*>(sensor2.get());
Patrick Venturea58197c2018-06-11 15:29:45 -0700423
424 std::string type = "unchecked";
425 mgr.addSensor(type, name1, std::move(sensor1));
426 EXPECT_EQ(mgr.getSensor(name1), sensor_ptr1);
427 mgr.addSensor(type, name2, std::move(sensor2));
428 EXPECT_EQ(mgr.getSensor(name2), sensor_ptr2);
429
430 // Now that the sensors exist, add them to the zone.
431 zone->addFanInput(name1);
432 zone->addFanInput(name2);
433
434 // Initialize Zone
435 zone->initializeCache();
436
437 ReadReturn r1;
438 r1.value = 10.0;
439 r1.updated = std::chrono::high_resolution_clock::now();
440 EXPECT_CALL(*sensor_ptr1, read()).WillOnce(Return(r1));
441
442 ReadReturn r2;
443 r2.value = 11.0;
444 r2.updated = std::chrono::high_resolution_clock::now();
445 EXPECT_CALL(*sensor_ptr2, read()).WillOnce(Return(r2));
446
447 // Method under test will read through each fan sensor for the zone and
448 // cache the values.
449 zone->updateFanTelemetry();
450
451 EXPECT_EQ(r1.value, zone->getCachedValue(name1));
452 EXPECT_EQ(r2.value, zone->getCachedValue(name2));
453}
454
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700455TEST_F(PidZoneTest, ThermalInput_ValueTimeoutEntersFailSafeMode)
456{
Patrick Venturea58197c2018-06-11 15:29:45 -0700457 // On the second updateSensors call, the updated timestamp will be beyond
458 // the timeout limit.
459
460 int64_t timeout = 1;
461
462 std::string name1 = "temp1";
Patrick Williams8c051122023-05-10 07:50:59 -0500463 std::unique_ptr<Sensor> sensor1 = std::make_unique<SensorMock>(name1,
464 timeout);
Patrick Venturee2ec0f62018-09-04 12:30:27 -0700465 SensorMock* sensor_ptr1 = reinterpret_cast<SensorMock*>(sensor1.get());
Patrick Venturea58197c2018-06-11 15:29:45 -0700466
467 std::string name2 = "temp2";
Patrick Williams8c051122023-05-10 07:50:59 -0500468 std::unique_ptr<Sensor> sensor2 = std::make_unique<SensorMock>(name2,
469 timeout);
Patrick Venturee2ec0f62018-09-04 12:30:27 -0700470 SensorMock* sensor_ptr2 = reinterpret_cast<SensorMock*>(sensor2.get());
Patrick Venturea58197c2018-06-11 15:29:45 -0700471
472 std::string type = "unchecked";
473 mgr.addSensor(type, name1, std::move(sensor1));
474 EXPECT_EQ(mgr.getSensor(name1), sensor_ptr1);
475 mgr.addSensor(type, name2, std::move(sensor2));
476 EXPECT_EQ(mgr.getSensor(name2), sensor_ptr2);
477
478 zone->addThermalInput(name1);
479 zone->addThermalInput(name2);
480
481 // Initialize Zone
482 zone->initializeCache();
483
484 // Verify now in failsafe mode.
485 EXPECT_TRUE(zone->getFailSafeMode());
486
487 ReadReturn r1;
488 r1.value = 10.0;
489 r1.updated = std::chrono::high_resolution_clock::now();
490 EXPECT_CALL(*sensor_ptr1, read()).WillOnce(Return(r1));
491
492 ReadReturn r2;
493 r2.value = 11.0;
494 r2.updated = std::chrono::high_resolution_clock::now();
495 EXPECT_CALL(*sensor_ptr2, read()).WillOnce(Return(r2));
496
497 zone->updateSensors();
498 EXPECT_FALSE(zone->getFailSafeMode());
499
500 // Ok, so we're not in failsafe mode, so let's set updated to the past.
501 // sensor1 will have an updated field older than its timeout value, but
502 // sensor2 will be fine. :D
503 r1.updated -= std::chrono::seconds(3);
504 r2.updated = std::chrono::high_resolution_clock::now();
505
506 EXPECT_CALL(*sensor_ptr1, read()).WillOnce(Return(r1));
507 EXPECT_CALL(*sensor_ptr2, read()).WillOnce(Return(r2));
508
509 // Method under test will read each sensor. One sensor's value is older
510 // than the timeout for that sensor and this triggers failsafe mode.
511 zone->updateSensors();
512 EXPECT_TRUE(zone->getFailSafeMode());
513}
514
Will Liangded0ab52019-05-15 17:10:06 +0800515TEST_F(PidZoneTest, FanInputTest_FailsafeToValid_ReadsSensors)
516{
517 // This will add a couple fan inputs, and verify the values are cached.
518
519 std::string name1 = "fan1";
520 int64_t timeout = 2;
521
Patrick Williams8c051122023-05-10 07:50:59 -0500522 std::unique_ptr<Sensor> sensor1 = std::make_unique<SensorMock>(name1,
523 timeout);
Will Liangded0ab52019-05-15 17:10:06 +0800524 SensorMock* sensor_ptr1 = reinterpret_cast<SensorMock*>(sensor1.get());
525
526 std::string name2 = "fan2";
Patrick Williams8c051122023-05-10 07:50:59 -0500527 std::unique_ptr<Sensor> sensor2 = std::make_unique<SensorMock>(name2,
528 timeout);
Will Liangded0ab52019-05-15 17:10:06 +0800529 SensorMock* sensor_ptr2 = reinterpret_cast<SensorMock*>(sensor2.get());
530
531 std::string type = "unchecked";
532 mgr.addSensor(type, name1, std::move(sensor1));
533 EXPECT_EQ(mgr.getSensor(name1), sensor_ptr1);
534 mgr.addSensor(type, name2, std::move(sensor2));
535 EXPECT_EQ(mgr.getSensor(name2), sensor_ptr2);
536
537 // Now that the sensors exist, add them to the zone.
538 zone->addFanInput(name1);
539 zone->addFanInput(name2);
540
541 // Initialize Zone
542 zone->initializeCache();
543
544 // Verify now in failsafe mode.
545 EXPECT_TRUE(zone->getFailSafeMode());
546
547 ReadReturn r1;
548 r1.value = 10.0;
549 r1.updated = std::chrono::high_resolution_clock::now();
550 EXPECT_CALL(*sensor_ptr1, read()).WillOnce(Return(r1));
551
552 ReadReturn r2;
553 r2.value = 11.0;
554 r2.updated = std::chrono::high_resolution_clock::now();
555 EXPECT_CALL(*sensor_ptr2, read()).WillOnce(Return(r2));
556
557 // Method under test will read through each fan sensor for the zone and
558 // cache the values.
559 zone->updateFanTelemetry();
560
561 // We should no longer be in failsafe mode.
562 EXPECT_FALSE(zone->getFailSafeMode());
563
564 EXPECT_EQ(r1.value, zone->getCachedValue(name1));
565 EXPECT_EQ(r2.value, zone->getCachedValue(name2));
566}
567
568TEST_F(PidZoneTest, FanInputTest_ValueTimeoutEntersFailSafeMode)
569{
570 // This will add a couple fan inputs, and verify the values are cached.
571
572 std::string name1 = "fan1";
573 int64_t timeout = 2;
574
Patrick Williams8c051122023-05-10 07:50:59 -0500575 std::unique_ptr<Sensor> sensor1 = std::make_unique<SensorMock>(name1,
576 timeout);
Will Liangded0ab52019-05-15 17:10:06 +0800577 SensorMock* sensor_ptr1 = reinterpret_cast<SensorMock*>(sensor1.get());
578
579 std::string name2 = "fan2";
Patrick Williams8c051122023-05-10 07:50:59 -0500580 std::unique_ptr<Sensor> sensor2 = std::make_unique<SensorMock>(name2,
581 timeout);
Will Liangded0ab52019-05-15 17:10:06 +0800582 SensorMock* sensor_ptr2 = reinterpret_cast<SensorMock*>(sensor2.get());
583
584 std::string type = "unchecked";
585 mgr.addSensor(type, name1, std::move(sensor1));
586 EXPECT_EQ(mgr.getSensor(name1), sensor_ptr1);
587 mgr.addSensor(type, name2, std::move(sensor2));
588 EXPECT_EQ(mgr.getSensor(name2), sensor_ptr2);
589
590 // Now that the sensors exist, add them to the zone.
591 zone->addFanInput(name1);
592 zone->addFanInput(name2);
593
594 // Initialize Zone
595 zone->initializeCache();
596
597 // Verify now in failsafe mode.
598 EXPECT_TRUE(zone->getFailSafeMode());
599
600 ReadReturn r1;
601 r1.value = 10.0;
602 r1.updated = std::chrono::high_resolution_clock::now();
603 EXPECT_CALL(*sensor_ptr1, read()).WillOnce(Return(r1));
604
605 ReadReturn r2;
606 r2.value = 11.0;
607 r2.updated = std::chrono::high_resolution_clock::now();
608 EXPECT_CALL(*sensor_ptr2, read()).WillOnce(Return(r2));
609
610 // Method under test will read through each fan sensor for the zone and
611 // cache the values.
612 zone->updateFanTelemetry();
613
614 // We should no longer be in failsafe mode.
615 EXPECT_FALSE(zone->getFailSafeMode());
616
617 r1.updated -= std::chrono::seconds(3);
618 r2.updated = std::chrono::high_resolution_clock::now();
619
620 EXPECT_CALL(*sensor_ptr1, read()).WillOnce(Return(r1));
621 EXPECT_CALL(*sensor_ptr2, read()).WillOnce(Return(r2));
622
623 zone->updateFanTelemetry();
624 EXPECT_TRUE(zone->getFailSafeMode());
625}
626
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700627TEST_F(PidZoneTest, GetSensorTest_ReturnsExpected)
628{
Patrick Venturea58197c2018-06-11 15:29:45 -0700629 // One can grab a sensor from the manager through the zone.
630
631 int64_t timeout = 1;
632
633 std::string name1 = "temp1";
Patrick Williams8c051122023-05-10 07:50:59 -0500634 std::unique_ptr<Sensor> sensor1 = std::make_unique<SensorMock>(name1,
635 timeout);
Patrick Venturee2ec0f62018-09-04 12:30:27 -0700636 SensorMock* sensor_ptr1 = reinterpret_cast<SensorMock*>(sensor1.get());
Patrick Venturea58197c2018-06-11 15:29:45 -0700637
638 std::string type = "unchecked";
639 mgr.addSensor(type, name1, std::move(sensor1));
640 EXPECT_EQ(mgr.getSensor(name1), sensor_ptr1);
641
642 zone->addThermalInput(name1);
643
644 // Verify method under test returns the pointer we expect.
645 EXPECT_EQ(mgr.getSensor(name1), zone->getSensor(name1));
646}
647
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700648TEST_F(PidZoneTest, AddThermalPIDTest_VerifiesThermalPIDsProcessed)
649{
Patrick Venturea58197c2018-06-11 15:29:45 -0700650 // Tests adding a thermal PID controller to the zone, and verifies it's
651 // touched during processing.
652
653 std::unique_ptr<PIDController> tpid =
654 std::make_unique<ControllerMock>("thermal1", zone.get());
Patrick Venturee2ec0f62018-09-04 12:30:27 -0700655 ControllerMock* tmock = reinterpret_cast<ControllerMock*>(tpid.get());
Patrick Venturea58197c2018-06-11 15:29:45 -0700656
657 // Access the internal pid configuration to clear it out (unrelated to the
658 // test).
Patrick Venture563a3562018-10-30 09:31:26 -0700659 ec::pid_info_t* info = tpid->getPIDInfo();
Patrick Venturea58197c2018-06-11 15:29:45 -0700660 std::memset(info, 0x00, sizeof(ec::pid_info_t));
661
662 zone->addThermalPID(std::move(tpid));
663
Patrick Venture563a3562018-10-30 09:31:26 -0700664 EXPECT_CALL(*tmock, setptProc()).WillOnce(Return(10.0));
665 EXPECT_CALL(*tmock, inputProc()).WillOnce(Return(11.0));
666 EXPECT_CALL(*tmock, outputProc(_));
Patrick Venturea58197c2018-06-11 15:29:45 -0700667
668 // Method under test will, for each thermal PID, call setpt, input, and
669 // output.
Patrick Venture563a3562018-10-30 09:31:26 -0700670 zone->processThermals();
Patrick Venturea58197c2018-06-11 15:29:45 -0700671}
672
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700673TEST_F(PidZoneTest, AddFanPIDTest_VerifiesFanPIDsProcessed)
674{
Patrick Venturea58197c2018-06-11 15:29:45 -0700675 // Tests adding a fan PID controller to the zone, and verifies it's
676 // touched during processing.
677
678 std::unique_ptr<PIDController> tpid =
679 std::make_unique<ControllerMock>("fan1", zone.get());
Patrick Venturee2ec0f62018-09-04 12:30:27 -0700680 ControllerMock* tmock = reinterpret_cast<ControllerMock*>(tpid.get());
Patrick Venturea58197c2018-06-11 15:29:45 -0700681
682 // Access the internal pid configuration to clear it out (unrelated to the
683 // test).
Patrick Venture563a3562018-10-30 09:31:26 -0700684 ec::pid_info_t* info = tpid->getPIDInfo();
Patrick Venturea58197c2018-06-11 15:29:45 -0700685 std::memset(info, 0x00, sizeof(ec::pid_info_t));
686
687 zone->addFanPID(std::move(tpid));
688
Patrick Venture563a3562018-10-30 09:31:26 -0700689 EXPECT_CALL(*tmock, setptProc()).WillOnce(Return(10.0));
690 EXPECT_CALL(*tmock, inputProc()).WillOnce(Return(11.0));
691 EXPECT_CALL(*tmock, outputProc(_));
Patrick Venturea58197c2018-06-11 15:29:45 -0700692
693 // Method under test will, for each fan PID, call setpt, input, and output.
Patrick Venture563a3562018-10-30 09:31:26 -0700694 zone->processFans();
Patrick Venturea58197c2018-06-11 15:29:45 -0700695}
696
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700697TEST_F(PidZoneTest, ManualModeDbusTest_VerifySetManualBehavesAsExpected)
698{
Patrick Venturea58197c2018-06-11 15:29:45 -0700699 // The manual(bool) method is inherited from the dbus mode interface.
700
701 // Verifies that someone doesn't remove the internal call to the dbus
702 // object from which we're inheriting.
703 EXPECT_CALL(sdbus_mock_mode,
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700704 sd_bus_emit_properties_changed_strv(
705 IsNull(), StrEq(objPath), StrEq(modeInterface), NotNull()))
Harvey.Wua1ae4fa2022-10-28 17:38:35 +0800706 .WillOnce(Invoke(
707 [&]([[maybe_unused]] sd_bus* bus, [[maybe_unused]] const char* path,
708 [[maybe_unused]] const char* interface, const char** names) {
Patrick Williams8c051122023-05-10 07:50:59 -0500709 EXPECT_STREQ("Manual", names[0]);
710 return 0;
Patrick Williamse1dbb592023-10-20 11:19:22 -0500711 }));
Patrick Venturea58197c2018-06-11 15:29:45 -0700712
713 // Method under test will set the manual mode to true and broadcast this
714 // change on dbus.
715 zone->manual(true);
716 EXPECT_TRUE(zone->getManualMode());
717}
718
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700719TEST_F(PidZoneTest, FailsafeDbusTest_VerifiesReturnsExpected)
720{
Patrick Venturea58197c2018-06-11 15:29:45 -0700721 // This property is implemented by us as read-only, such that trying to
722 // write to it will have no effect.
723 EXPECT_EQ(zone->failSafe(), zone->getFailSafeMode());
724}
Patrick Venturea0764872020-08-08 07:48:43 -0700725
726} // namespace
727} // namespace pid_control