blob: 8c55a70c3f2e8be7e9ff29ed4680804885b2a002 [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;
ykchiu9fe3a3c2023-05-11 13:43:54 +080056 double failSafePercent = 0;
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;
ykchiu9fe3a3c2023-05-11 13:43:54 +0800125 double failSafePercent = 0;
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.
ykchiu9fe3a3c2023-05-11 13:43:54 +0800294 // when the final failsafe percent is zero , it indicate
295 // no failsafe percent is configured  , set it to 100% as
296 // the default setting.
297
298 std::vector<double> values = {0, 0, 0};
299 int64_t defaultPercent = 100;
300
301 zone->addPidFailSafePercent("temp1", values[0]);
302 zone->addPidFailSafePercent("temp2", values[1]);
303 zone->addPidFailSafePercent("temp3", values[2]);
304
305 zone->initPidFailSafePercent();
306
307 EXPECT_EQ(defaultPercent, zone->getFailSafePercent());
308}
309
310TEST_F(PidZoneTest, GetFailSafePercent_VerifyReturnsExpected)
311{
312 // Tests adding PID controller with FailSafePercent to the zone,
313 // and verifies it's returned as expected.
314
315 std::vector<double> values = {60, 80, 70};
316 double max_value = 0;
317
318 for (const auto& value : values)
319 {
320 max_value = std::max(max_value, value);
321 }
322
323 zone->addPidFailSafePercent("temp1", values[0]);
324 zone->addPidFailSafePercent("temp2", values[1]);
325 zone->addPidFailSafePercent("temp3", values[2]);
326
327 zone->initPidFailSafePercent();
328
329 EXPECT_EQ(max_value, zone->getFailSafePercent());
Patrick Venturea58197c2018-06-11 15:29:45 -0700330}
331
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700332TEST_F(PidZoneTest, ThermalInputs_FailsafeToValid_ReadsSensors)
333{
Patrick Venturea58197c2018-06-11 15:29:45 -0700334 // This test will add a couple thermal inputs, and verify that the zone
335 // initializes into failsafe mode, and will read each sensor.
336
337 std::string name1 = "temp1";
338 int64_t timeout = 1;
339
Patrick Williams8c051122023-05-10 07:50:59 -0500340 std::unique_ptr<Sensor> sensor1 = std::make_unique<SensorMock>(name1,
341 timeout);
Patrick Venturee2ec0f62018-09-04 12:30:27 -0700342 SensorMock* sensor_ptr1 = reinterpret_cast<SensorMock*>(sensor1.get());
Patrick Venturea58197c2018-06-11 15:29:45 -0700343
344 std::string name2 = "temp2";
Patrick Williams8c051122023-05-10 07:50:59 -0500345 std::unique_ptr<Sensor> sensor2 = std::make_unique<SensorMock>(name2,
346 timeout);
Patrick Venturee2ec0f62018-09-04 12:30:27 -0700347 SensorMock* sensor_ptr2 = reinterpret_cast<SensorMock*>(sensor2.get());
Patrick Venturea58197c2018-06-11 15:29:45 -0700348
349 std::string type = "unchecked";
350 mgr.addSensor(type, name1, std::move(sensor1));
351 EXPECT_EQ(mgr.getSensor(name1), sensor_ptr1);
352 mgr.addSensor(type, name2, std::move(sensor2));
353 EXPECT_EQ(mgr.getSensor(name2), sensor_ptr2);
354
355 // Now that the sensors exist, add them to the zone.
356 zone->addThermalInput(name1);
357 zone->addThermalInput(name2);
358
359 // Initialize Zone
360 zone->initializeCache();
361
362 // Verify now in failsafe mode.
363 EXPECT_TRUE(zone->getFailSafeMode());
364
365 ReadReturn r1;
366 r1.value = 10.0;
367 r1.updated = std::chrono::high_resolution_clock::now();
368 EXPECT_CALL(*sensor_ptr1, read()).WillOnce(Return(r1));
369
370 ReadReturn r2;
371 r2.value = 11.0;
372 r2.updated = std::chrono::high_resolution_clock::now();
373 EXPECT_CALL(*sensor_ptr2, read()).WillOnce(Return(r2));
374
375 // Read the sensors, this will put the values into the cache.
376 zone->updateSensors();
377
378 // We should no longer be in failsafe mode.
379 EXPECT_FALSE(zone->getFailSafeMode());
380
381 EXPECT_EQ(r1.value, zone->getCachedValue(name1));
382 EXPECT_EQ(r2.value, zone->getCachedValue(name2));
383}
384
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700385TEST_F(PidZoneTest, FanInputTest_VerifiesFanValuesCached)
386{
Patrick Venturea58197c2018-06-11 15:29:45 -0700387 // This will add a couple fan inputs, and verify the values are cached.
388
389 std::string name1 = "fan1";
390 int64_t timeout = 2;
391
Patrick Williams8c051122023-05-10 07:50:59 -0500392 std::unique_ptr<Sensor> sensor1 = std::make_unique<SensorMock>(name1,
393 timeout);
Patrick Venturee2ec0f62018-09-04 12:30:27 -0700394 SensorMock* sensor_ptr1 = reinterpret_cast<SensorMock*>(sensor1.get());
Patrick Venturea58197c2018-06-11 15:29:45 -0700395
396 std::string name2 = "fan2";
Patrick Williams8c051122023-05-10 07:50:59 -0500397 std::unique_ptr<Sensor> sensor2 = std::make_unique<SensorMock>(name2,
398 timeout);
Patrick Venturee2ec0f62018-09-04 12:30:27 -0700399 SensorMock* sensor_ptr2 = reinterpret_cast<SensorMock*>(sensor2.get());
Patrick Venturea58197c2018-06-11 15:29:45 -0700400
401 std::string type = "unchecked";
402 mgr.addSensor(type, name1, std::move(sensor1));
403 EXPECT_EQ(mgr.getSensor(name1), sensor_ptr1);
404 mgr.addSensor(type, name2, std::move(sensor2));
405 EXPECT_EQ(mgr.getSensor(name2), sensor_ptr2);
406
407 // Now that the sensors exist, add them to the zone.
408 zone->addFanInput(name1);
409 zone->addFanInput(name2);
410
411 // Initialize Zone
412 zone->initializeCache();
413
414 ReadReturn r1;
415 r1.value = 10.0;
416 r1.updated = std::chrono::high_resolution_clock::now();
417 EXPECT_CALL(*sensor_ptr1, read()).WillOnce(Return(r1));
418
419 ReadReturn r2;
420 r2.value = 11.0;
421 r2.updated = std::chrono::high_resolution_clock::now();
422 EXPECT_CALL(*sensor_ptr2, read()).WillOnce(Return(r2));
423
424 // Method under test will read through each fan sensor for the zone and
425 // cache the values.
426 zone->updateFanTelemetry();
427
428 EXPECT_EQ(r1.value, zone->getCachedValue(name1));
429 EXPECT_EQ(r2.value, zone->getCachedValue(name2));
430}
431
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700432TEST_F(PidZoneTest, ThermalInput_ValueTimeoutEntersFailSafeMode)
433{
Patrick Venturea58197c2018-06-11 15:29:45 -0700434 // On the second updateSensors call, the updated timestamp will be beyond
435 // the timeout limit.
436
437 int64_t timeout = 1;
438
439 std::string name1 = "temp1";
Patrick Williams8c051122023-05-10 07:50:59 -0500440 std::unique_ptr<Sensor> sensor1 = std::make_unique<SensorMock>(name1,
441 timeout);
Patrick Venturee2ec0f62018-09-04 12:30:27 -0700442 SensorMock* sensor_ptr1 = reinterpret_cast<SensorMock*>(sensor1.get());
Patrick Venturea58197c2018-06-11 15:29:45 -0700443
444 std::string name2 = "temp2";
Patrick Williams8c051122023-05-10 07:50:59 -0500445 std::unique_ptr<Sensor> sensor2 = std::make_unique<SensorMock>(name2,
446 timeout);
Patrick Venturee2ec0f62018-09-04 12:30:27 -0700447 SensorMock* sensor_ptr2 = reinterpret_cast<SensorMock*>(sensor2.get());
Patrick Venturea58197c2018-06-11 15:29:45 -0700448
449 std::string type = "unchecked";
450 mgr.addSensor(type, name1, std::move(sensor1));
451 EXPECT_EQ(mgr.getSensor(name1), sensor_ptr1);
452 mgr.addSensor(type, name2, std::move(sensor2));
453 EXPECT_EQ(mgr.getSensor(name2), sensor_ptr2);
454
455 zone->addThermalInput(name1);
456 zone->addThermalInput(name2);
457
458 // Initialize Zone
459 zone->initializeCache();
460
461 // Verify now in failsafe mode.
462 EXPECT_TRUE(zone->getFailSafeMode());
463
464 ReadReturn r1;
465 r1.value = 10.0;
466 r1.updated = std::chrono::high_resolution_clock::now();
467 EXPECT_CALL(*sensor_ptr1, read()).WillOnce(Return(r1));
468
469 ReadReturn r2;
470 r2.value = 11.0;
471 r2.updated = std::chrono::high_resolution_clock::now();
472 EXPECT_CALL(*sensor_ptr2, read()).WillOnce(Return(r2));
473
474 zone->updateSensors();
475 EXPECT_FALSE(zone->getFailSafeMode());
476
477 // Ok, so we're not in failsafe mode, so let's set updated to the past.
478 // sensor1 will have an updated field older than its timeout value, but
479 // sensor2 will be fine. :D
480 r1.updated -= std::chrono::seconds(3);
481 r2.updated = std::chrono::high_resolution_clock::now();
482
483 EXPECT_CALL(*sensor_ptr1, read()).WillOnce(Return(r1));
484 EXPECT_CALL(*sensor_ptr2, read()).WillOnce(Return(r2));
485
486 // Method under test will read each sensor. One sensor's value is older
487 // than the timeout for that sensor and this triggers failsafe mode.
488 zone->updateSensors();
489 EXPECT_TRUE(zone->getFailSafeMode());
490}
491
Will Liangded0ab52019-05-15 17:10:06 +0800492TEST_F(PidZoneTest, FanInputTest_FailsafeToValid_ReadsSensors)
493{
494 // This will add a couple fan inputs, and verify the values are cached.
495
496 std::string name1 = "fan1";
497 int64_t timeout = 2;
498
Patrick Williams8c051122023-05-10 07:50:59 -0500499 std::unique_ptr<Sensor> sensor1 = std::make_unique<SensorMock>(name1,
500 timeout);
Will Liangded0ab52019-05-15 17:10:06 +0800501 SensorMock* sensor_ptr1 = reinterpret_cast<SensorMock*>(sensor1.get());
502
503 std::string name2 = "fan2";
Patrick Williams8c051122023-05-10 07:50:59 -0500504 std::unique_ptr<Sensor> sensor2 = std::make_unique<SensorMock>(name2,
505 timeout);
Will Liangded0ab52019-05-15 17:10:06 +0800506 SensorMock* sensor_ptr2 = reinterpret_cast<SensorMock*>(sensor2.get());
507
508 std::string type = "unchecked";
509 mgr.addSensor(type, name1, std::move(sensor1));
510 EXPECT_EQ(mgr.getSensor(name1), sensor_ptr1);
511 mgr.addSensor(type, name2, std::move(sensor2));
512 EXPECT_EQ(mgr.getSensor(name2), sensor_ptr2);
513
514 // Now that the sensors exist, add them to the zone.
515 zone->addFanInput(name1);
516 zone->addFanInput(name2);
517
518 // Initialize Zone
519 zone->initializeCache();
520
521 // Verify now in failsafe mode.
522 EXPECT_TRUE(zone->getFailSafeMode());
523
524 ReadReturn r1;
525 r1.value = 10.0;
526 r1.updated = std::chrono::high_resolution_clock::now();
527 EXPECT_CALL(*sensor_ptr1, read()).WillOnce(Return(r1));
528
529 ReadReturn r2;
530 r2.value = 11.0;
531 r2.updated = std::chrono::high_resolution_clock::now();
532 EXPECT_CALL(*sensor_ptr2, read()).WillOnce(Return(r2));
533
534 // Method under test will read through each fan sensor for the zone and
535 // cache the values.
536 zone->updateFanTelemetry();
537
538 // We should no longer be in failsafe mode.
539 EXPECT_FALSE(zone->getFailSafeMode());
540
541 EXPECT_EQ(r1.value, zone->getCachedValue(name1));
542 EXPECT_EQ(r2.value, zone->getCachedValue(name2));
543}
544
545TEST_F(PidZoneTest, FanInputTest_ValueTimeoutEntersFailSafeMode)
546{
547 // This will add a couple fan inputs, and verify the values are cached.
548
549 std::string name1 = "fan1";
550 int64_t timeout = 2;
551
Patrick Williams8c051122023-05-10 07:50:59 -0500552 std::unique_ptr<Sensor> sensor1 = std::make_unique<SensorMock>(name1,
553 timeout);
Will Liangded0ab52019-05-15 17:10:06 +0800554 SensorMock* sensor_ptr1 = reinterpret_cast<SensorMock*>(sensor1.get());
555
556 std::string name2 = "fan2";
Patrick Williams8c051122023-05-10 07:50:59 -0500557 std::unique_ptr<Sensor> sensor2 = std::make_unique<SensorMock>(name2,
558 timeout);
Will Liangded0ab52019-05-15 17:10:06 +0800559 SensorMock* sensor_ptr2 = reinterpret_cast<SensorMock*>(sensor2.get());
560
561 std::string type = "unchecked";
562 mgr.addSensor(type, name1, std::move(sensor1));
563 EXPECT_EQ(mgr.getSensor(name1), sensor_ptr1);
564 mgr.addSensor(type, name2, std::move(sensor2));
565 EXPECT_EQ(mgr.getSensor(name2), sensor_ptr2);
566
567 // Now that the sensors exist, add them to the zone.
568 zone->addFanInput(name1);
569 zone->addFanInput(name2);
570
571 // Initialize Zone
572 zone->initializeCache();
573
574 // Verify now in failsafe mode.
575 EXPECT_TRUE(zone->getFailSafeMode());
576
577 ReadReturn r1;
578 r1.value = 10.0;
579 r1.updated = std::chrono::high_resolution_clock::now();
580 EXPECT_CALL(*sensor_ptr1, read()).WillOnce(Return(r1));
581
582 ReadReturn r2;
583 r2.value = 11.0;
584 r2.updated = std::chrono::high_resolution_clock::now();
585 EXPECT_CALL(*sensor_ptr2, read()).WillOnce(Return(r2));
586
587 // Method under test will read through each fan sensor for the zone and
588 // cache the values.
589 zone->updateFanTelemetry();
590
591 // We should no longer be in failsafe mode.
592 EXPECT_FALSE(zone->getFailSafeMode());
593
594 r1.updated -= std::chrono::seconds(3);
595 r2.updated = std::chrono::high_resolution_clock::now();
596
597 EXPECT_CALL(*sensor_ptr1, read()).WillOnce(Return(r1));
598 EXPECT_CALL(*sensor_ptr2, read()).WillOnce(Return(r2));
599
600 zone->updateFanTelemetry();
601 EXPECT_TRUE(zone->getFailSafeMode());
602}
603
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700604TEST_F(PidZoneTest, GetSensorTest_ReturnsExpected)
605{
Patrick Venturea58197c2018-06-11 15:29:45 -0700606 // One can grab a sensor from the manager through the zone.
607
608 int64_t timeout = 1;
609
610 std::string name1 = "temp1";
Patrick Williams8c051122023-05-10 07:50:59 -0500611 std::unique_ptr<Sensor> sensor1 = std::make_unique<SensorMock>(name1,
612 timeout);
Patrick Venturee2ec0f62018-09-04 12:30:27 -0700613 SensorMock* sensor_ptr1 = reinterpret_cast<SensorMock*>(sensor1.get());
Patrick Venturea58197c2018-06-11 15:29:45 -0700614
615 std::string type = "unchecked";
616 mgr.addSensor(type, name1, std::move(sensor1));
617 EXPECT_EQ(mgr.getSensor(name1), sensor_ptr1);
618
619 zone->addThermalInput(name1);
620
621 // Verify method under test returns the pointer we expect.
622 EXPECT_EQ(mgr.getSensor(name1), zone->getSensor(name1));
623}
624
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700625TEST_F(PidZoneTest, AddThermalPIDTest_VerifiesThermalPIDsProcessed)
626{
Patrick Venturea58197c2018-06-11 15:29:45 -0700627 // Tests adding a thermal PID controller to the zone, and verifies it's
628 // touched during processing.
629
630 std::unique_ptr<PIDController> tpid =
631 std::make_unique<ControllerMock>("thermal1", zone.get());
Patrick Venturee2ec0f62018-09-04 12:30:27 -0700632 ControllerMock* tmock = reinterpret_cast<ControllerMock*>(tpid.get());
Patrick Venturea58197c2018-06-11 15:29:45 -0700633
634 // Access the internal pid configuration to clear it out (unrelated to the
635 // test).
Patrick Venture563a3562018-10-30 09:31:26 -0700636 ec::pid_info_t* info = tpid->getPIDInfo();
Patrick Venturea58197c2018-06-11 15:29:45 -0700637 std::memset(info, 0x00, sizeof(ec::pid_info_t));
638
639 zone->addThermalPID(std::move(tpid));
640
Patrick Venture563a3562018-10-30 09:31:26 -0700641 EXPECT_CALL(*tmock, setptProc()).WillOnce(Return(10.0));
642 EXPECT_CALL(*tmock, inputProc()).WillOnce(Return(11.0));
643 EXPECT_CALL(*tmock, outputProc(_));
Patrick Venturea58197c2018-06-11 15:29:45 -0700644
645 // Method under test will, for each thermal PID, call setpt, input, and
646 // output.
Patrick Venture563a3562018-10-30 09:31:26 -0700647 zone->processThermals();
Patrick Venturea58197c2018-06-11 15:29:45 -0700648}
649
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700650TEST_F(PidZoneTest, AddFanPIDTest_VerifiesFanPIDsProcessed)
651{
Patrick Venturea58197c2018-06-11 15:29:45 -0700652 // Tests adding a fan PID controller to the zone, and verifies it's
653 // touched during processing.
654
655 std::unique_ptr<PIDController> tpid =
656 std::make_unique<ControllerMock>("fan1", zone.get());
Patrick Venturee2ec0f62018-09-04 12:30:27 -0700657 ControllerMock* tmock = reinterpret_cast<ControllerMock*>(tpid.get());
Patrick Venturea58197c2018-06-11 15:29:45 -0700658
659 // Access the internal pid configuration to clear it out (unrelated to the
660 // test).
Patrick Venture563a3562018-10-30 09:31:26 -0700661 ec::pid_info_t* info = tpid->getPIDInfo();
Patrick Venturea58197c2018-06-11 15:29:45 -0700662 std::memset(info, 0x00, sizeof(ec::pid_info_t));
663
664 zone->addFanPID(std::move(tpid));
665
Patrick Venture563a3562018-10-30 09:31:26 -0700666 EXPECT_CALL(*tmock, setptProc()).WillOnce(Return(10.0));
667 EXPECT_CALL(*tmock, inputProc()).WillOnce(Return(11.0));
668 EXPECT_CALL(*tmock, outputProc(_));
Patrick Venturea58197c2018-06-11 15:29:45 -0700669
670 // Method under test will, for each fan PID, call setpt, input, and output.
Patrick Venture563a3562018-10-30 09:31:26 -0700671 zone->processFans();
Patrick Venturea58197c2018-06-11 15:29:45 -0700672}
673
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700674TEST_F(PidZoneTest, ManualModeDbusTest_VerifySetManualBehavesAsExpected)
675{
Patrick Venturea58197c2018-06-11 15:29:45 -0700676 // The manual(bool) method is inherited from the dbus mode interface.
677
678 // Verifies that someone doesn't remove the internal call to the dbus
679 // object from which we're inheriting.
680 EXPECT_CALL(sdbus_mock_mode,
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700681 sd_bus_emit_properties_changed_strv(
682 IsNull(), StrEq(objPath), StrEq(modeInterface), NotNull()))
Harvey.Wua1ae4fa2022-10-28 17:38:35 +0800683 .WillOnce(Invoke(
684 [&]([[maybe_unused]] sd_bus* bus, [[maybe_unused]] const char* path,
685 [[maybe_unused]] const char* interface, const char** names) {
Patrick Williams8c051122023-05-10 07:50:59 -0500686 EXPECT_STREQ("Manual", names[0]);
687 return 0;
688 }));
Patrick Venturea58197c2018-06-11 15:29:45 -0700689
690 // Method under test will set the manual mode to true and broadcast this
691 // change on dbus.
692 zone->manual(true);
693 EXPECT_TRUE(zone->getManualMode());
694}
695
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700696TEST_F(PidZoneTest, FailsafeDbusTest_VerifiesReturnsExpected)
697{
Patrick Venturea58197c2018-06-11 15:29:45 -0700698 // This property is implemented by us as read-only, such that trying to
699 // write to it will have no effect.
700 EXPECT_EQ(zone->failSafe(), zone->getFailSafeMode());
701}
Patrick Venturea0764872020-08-08 07:48:43 -0700702
703} // namespace
704} // namespace pid_control