blob: 3a5871d48d73a82ca50388b65e0c0863ea1b30d1 [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";
29
Patrick Ventureda4a5dd2018-08-31 09:42:48 -070030namespace
31{
Patrick Venturea58197c2018-06-11 15:29:45 -070032
Patrick Ventureda4a5dd2018-08-31 09:42:48 -070033TEST(PidZoneConstructorTest, BoringConstructorTest)
34{
Patrick Venturea58197c2018-06-11 15:29:45 -070035 // Build a PID Zone.
36
37 sdbusplus::SdBusMock sdbus_mock_passive, sdbus_mock_host, sdbus_mock_mode;
38 auto bus_mock_passive = sdbusplus::get_mocked_new(&sdbus_mock_passive);
39 auto bus_mock_host = sdbusplus::get_mocked_new(&sdbus_mock_host);
40 auto bus_mock_mode = sdbusplus::get_mocked_new(&sdbus_mock_mode);
41
42 EXPECT_CALL(sdbus_mock_host,
43 sd_bus_add_object_manager(
Patrick Ventureda4a5dd2018-08-31 09:42:48 -070044 IsNull(), _, StrEq("/xyz/openbmc_project/extsensors")))
Patrick Venturea58197c2018-06-11 15:29:45 -070045 .WillOnce(Return(0));
46
James Feist1fe08952019-05-07 09:17:16 -070047 SensorManager m(bus_mock_passive, bus_mock_host);
Patrick Venturea58197c2018-06-11 15:29:45 -070048
49 bool defer = true;
Patrick Venturee2ec0f62018-09-04 12:30:27 -070050 const char* objPath = "/path/";
Patrick Venturea58197c2018-06-11 15:29:45 -070051 int64_t zone = 1;
James Feist3484bed2019-02-25 13:28:18 -080052 double minThermalOutput = 1000.0;
Patrick Venture5f59c0f2018-11-11 12:55:14 -080053 double failSafePercent = 0.75;
Bonnie Lo0e8fc392022-10-05 10:20:55 +080054 conf::CycleTime cycleTime;
Patrick Venturea58197c2018-06-11 15:29:45 -070055
James Feist0709e2f2020-07-08 10:59:45 -070056 double d;
Patrick Venturea58197c2018-06-11 15:29:45 -070057 std::vector<std::string> properties;
Patrick Ventureda4a5dd2018-08-31 09:42:48 -070058 SetupDbusObject(&sdbus_mock_mode, defer, objPath, modeInterface, properties,
James Feist0709e2f2020-07-08 10:59:45 -070059 &d);
Patrick Venturea58197c2018-06-11 15:29:45 -070060
Bonnie Lo0e8fc392022-10-05 10:20:55 +080061 DbusPidZone p(zone, minThermalOutput, failSafePercent, cycleTime, m,
62 bus_mock_mode, objPath, defer);
Patrick Venturea58197c2018-06-11 15:29:45 -070063 // Success.
64}
65
Patrick Ventureda4a5dd2018-08-31 09:42:48 -070066} // namespace
Patrick Venturea58197c2018-06-11 15:29:45 -070067
Patrick Ventureda4a5dd2018-08-31 09:42:48 -070068class PidZoneTest : public ::testing::Test
69{
70 protected:
71 PidZoneTest() :
72 property_index(), properties(), sdbus_mock_passive(), sdbus_mock_host(),
73 sdbus_mock_mode()
74 {
75 EXPECT_CALL(sdbus_mock_host,
76 sd_bus_add_object_manager(
77 IsNull(), _, StrEq("/xyz/openbmc_project/extsensors")))
78 .WillOnce(Return(0));
Patrick Venturea58197c2018-06-11 15:29:45 -070079
Patrick Ventureda4a5dd2018-08-31 09:42:48 -070080 auto bus_mock_passive = sdbusplus::get_mocked_new(&sdbus_mock_passive);
81 auto bus_mock_host = sdbusplus::get_mocked_new(&sdbus_mock_host);
82 auto bus_mock_mode = sdbusplus::get_mocked_new(&sdbus_mock_mode);
Patrick Venturea58197c2018-06-11 15:29:45 -070083
Patrick Ventureda4a5dd2018-08-31 09:42:48 -070084 // Compiler weirdly not happy about just instantiating mgr(...);
James Feist1fe08952019-05-07 09:17:16 -070085 SensorManager m(bus_mock_passive, bus_mock_host);
Patrick Ventureda4a5dd2018-08-31 09:42:48 -070086 mgr = std::move(m);
Patrick Venturea58197c2018-06-11 15:29:45 -070087
Patrick Ventureda4a5dd2018-08-31 09:42:48 -070088 SetupDbusObject(&sdbus_mock_mode, defer, objPath, modeInterface,
89 properties, &property_index);
Patrick Venturea58197c2018-06-11 15:29:45 -070090
Patrick Venture597ebd62020-08-11 08:48:19 -070091 zone = std::make_unique<DbusPidZone>(zoneId, minThermalOutput,
Bonnie Lo0e8fc392022-10-05 10:20:55 +080092 failSafePercent, cycleTime, mgr,
Patrick Venture597ebd62020-08-11 08:48:19 -070093 bus_mock_mode, objPath, defer);
Patrick Ventureda4a5dd2018-08-31 09:42:48 -070094 }
Patrick Venturea58197c2018-06-11 15:29:45 -070095
Patrick Ventureda4a5dd2018-08-31 09:42:48 -070096 // unused
James Feist0709e2f2020-07-08 10:59:45 -070097 double property_index;
Patrick Ventureda4a5dd2018-08-31 09:42:48 -070098 std::vector<std::string> properties;
Patrick Venturea58197c2018-06-11 15:29:45 -070099
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700100 sdbusplus::SdBusMock sdbus_mock_passive;
101 sdbusplus::SdBusMock sdbus_mock_host;
102 sdbusplus::SdBusMock sdbus_mock_mode;
103 int64_t zoneId = 1;
James Feist3484bed2019-02-25 13:28:18 -0800104 double minThermalOutput = 1000.0;
Patrick Venture5f59c0f2018-11-11 12:55:14 -0800105 double failSafePercent = 0.75;
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700106 bool defer = true;
Patrick Venturee2ec0f62018-09-04 12:30:27 -0700107 const char* objPath = "/path/";
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700108 SensorManager mgr;
Bonnie Lo0e8fc392022-10-05 10:20:55 +0800109 conf::CycleTime cycleTime;
Patrick Venturea58197c2018-06-11 15:29:45 -0700110
Patrick Venture597ebd62020-08-11 08:48:19 -0700111 std::unique_ptr<DbusPidZone> zone;
Patrick Venturea58197c2018-06-11 15:29:45 -0700112};
113
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700114TEST_F(PidZoneTest, GetZoneId_ReturnsExpected)
115{
Patrick Venturea58197c2018-06-11 15:29:45 -0700116 // Verifies the zoneId returned is what we expect.
117
Patrick Venture0bbeaf82018-10-30 18:50:31 -0700118 EXPECT_EQ(zoneId, zone->getZoneID());
Patrick Venturea58197c2018-06-11 15:29:45 -0700119}
120
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700121TEST_F(PidZoneTest, GetAndSetManualModeTest_BehavesAsExpected)
122{
Patrick Venturea58197c2018-06-11 15:29:45 -0700123 // Verifies that the zone starts in manual mode. Verifies that one can set
124 // the mode.
125 EXPECT_FALSE(zone->getManualMode());
126
127 zone->setManualMode(true);
128 EXPECT_TRUE(zone->getManualMode());
129}
130
Josh Lehana4146eb2020-10-01 11:49:09 -0700131TEST_F(PidZoneTest, SetManualMode_RedundantWritesEnabledOnceAfterManualMode)
132{
133 // Tests adding a fan PID controller to the zone, and verifies it's
134 // touched during processing.
135
136 std::unique_ptr<PIDController> tpid =
137 std::make_unique<ControllerMock>("fan1", zone.get());
138 ControllerMock* tmock = reinterpret_cast<ControllerMock*>(tpid.get());
139
140 // Access the internal pid configuration to clear it out (unrelated to the
141 // test).
142 ec::pid_info_t* info = tpid->getPIDInfo();
143 std::memset(info, 0x00, sizeof(ec::pid_info_t));
144
145 zone->addFanPID(std::move(tpid));
146
147 EXPECT_CALL(*tmock, setptProc()).WillOnce(Return(10.0));
148 EXPECT_CALL(*tmock, inputProc()).WillOnce(Return(11.0));
149 EXPECT_CALL(*tmock, outputProc(_));
150
151 // while zone is in auto mode redundant writes should be disabled
152 EXPECT_FALSE(zone->getRedundantWrite());
153
154 // but switching from manual to auto enables a single redundant write
155 zone->setManualMode(true);
156 zone->setManualMode(false);
157 EXPECT_TRUE(zone->getRedundantWrite());
158
159 // after one iteration of a pid loop redundant write should be cleared
160 zone->processFans();
161 EXPECT_FALSE(zone->getRedundantWrite());
162}
163
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700164TEST_F(PidZoneTest, RpmSetPoints_AddMaxClear_BehaveAsExpected)
165{
Patrick Venturef7a2dd52019-07-16 14:31:13 -0700166 // Tests addSetPoint, clearSetPoints, determineMaxSetPointRequest
Nirav Shahccc8bb62022-02-17 21:06:51 -0800167 // and getMinThermalSetPoint.
Patrick Venturea58197c2018-06-11 15:29:45 -0700168
169 // At least one value must be above the minimum thermal setpoint used in
170 // the constructor otherwise it'll choose that value
Patrick Venture5f59c0f2018-11-11 12:55:14 -0800171 std::vector<double> values = {100, 200, 300, 400, 500, 5000};
Patrick Venturea58197c2018-06-11 15:29:45 -0700172 for (auto v : values)
173 {
Nirav Shahccc8bb62022-02-17 21:06:51 -0800174 zone->addSetPoint(v, "");
Patrick Venturea58197c2018-06-11 15:29:45 -0700175 }
176
177 // This will pull the maximum RPM setpoint request.
Patrick Venturef7a2dd52019-07-16 14:31:13 -0700178 zone->determineMaxSetPointRequest();
179 EXPECT_EQ(5000, zone->getMaxSetPointRequest());
Patrick Venturea58197c2018-06-11 15:29:45 -0700180
181 // Clear the values, so it'll choose the minimum thermal setpoint.
Patrick Venture9bbf3332019-07-16 10:50:37 -0700182 zone->clearSetPoints();
Patrick Venturea58197c2018-06-11 15:29:45 -0700183
184 // This will go through the RPM set point values and grab the maximum.
Patrick Venturef7a2dd52019-07-16 14:31:13 -0700185 zone->determineMaxSetPointRequest();
Nirav Shahccc8bb62022-02-17 21:06:51 -0800186 EXPECT_EQ(zone->getMinThermalSetPoint(), zone->getMaxSetPointRequest());
Patrick Venturea58197c2018-06-11 15:29:45 -0700187}
188
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700189TEST_F(PidZoneTest, RpmSetPoints_AddBelowMinimum_BehavesAsExpected)
190{
Patrick Venturea58197c2018-06-11 15:29:45 -0700191 // Tests adding several RPM setpoints, however, they're all lower than the
Patrick Venture7280e272019-02-11 10:45:32 -0800192 // configured minimal thermal setpoint RPM value.
Patrick Venturea58197c2018-06-11 15:29:45 -0700193
Patrick Venture5f59c0f2018-11-11 12:55:14 -0800194 std::vector<double> values = {100, 200, 300, 400, 500};
Patrick Venturea58197c2018-06-11 15:29:45 -0700195 for (auto v : values)
196 {
Nirav Shahccc8bb62022-02-17 21:06:51 -0800197 zone->addSetPoint(v, "");
Patrick Venturea58197c2018-06-11 15:29:45 -0700198 }
199
200 // This will pull the maximum RPM setpoint request.
Patrick Venturef7a2dd52019-07-16 14:31:13 -0700201 zone->determineMaxSetPointRequest();
Patrick Venturea58197c2018-06-11 15:29:45 -0700202
203 // Verifies the value returned in the minimal thermal rpm set point.
Nirav Shahccc8bb62022-02-17 21:06:51 -0800204 EXPECT_EQ(zone->getMinThermalSetPoint(), zone->getMaxSetPointRequest());
Patrick Venturea58197c2018-06-11 15:29:45 -0700205}
206
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700207TEST_F(PidZoneTest, GetFailSafePercent_ReturnsExpected)
208{
Patrick Venturea58197c2018-06-11 15:29:45 -0700209 // Verify the value used to create the object is stored.
210 EXPECT_EQ(failSafePercent, zone->getFailSafePercent());
211}
212
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700213TEST_F(PidZoneTest, ThermalInputs_FailsafeToValid_ReadsSensors)
214{
Patrick Venturea58197c2018-06-11 15:29:45 -0700215 // This test will add a couple thermal inputs, and verify that the zone
216 // initializes into failsafe mode, and will read each sensor.
217
218 std::string name1 = "temp1";
219 int64_t timeout = 1;
220
221 std::unique_ptr<Sensor> sensor1 =
222 std::make_unique<SensorMock>(name1, timeout);
Patrick Venturee2ec0f62018-09-04 12:30:27 -0700223 SensorMock* sensor_ptr1 = reinterpret_cast<SensorMock*>(sensor1.get());
Patrick Venturea58197c2018-06-11 15:29:45 -0700224
225 std::string name2 = "temp2";
226 std::unique_ptr<Sensor> sensor2 =
227 std::make_unique<SensorMock>(name2, timeout);
Patrick Venturee2ec0f62018-09-04 12:30:27 -0700228 SensorMock* sensor_ptr2 = reinterpret_cast<SensorMock*>(sensor2.get());
Patrick Venturea58197c2018-06-11 15:29:45 -0700229
230 std::string type = "unchecked";
231 mgr.addSensor(type, name1, std::move(sensor1));
232 EXPECT_EQ(mgr.getSensor(name1), sensor_ptr1);
233 mgr.addSensor(type, name2, std::move(sensor2));
234 EXPECT_EQ(mgr.getSensor(name2), sensor_ptr2);
235
236 // Now that the sensors exist, add them to the zone.
237 zone->addThermalInput(name1);
238 zone->addThermalInput(name2);
239
240 // Initialize Zone
241 zone->initializeCache();
242
243 // Verify now in failsafe mode.
244 EXPECT_TRUE(zone->getFailSafeMode());
245
246 ReadReturn r1;
247 r1.value = 10.0;
248 r1.updated = std::chrono::high_resolution_clock::now();
249 EXPECT_CALL(*sensor_ptr1, read()).WillOnce(Return(r1));
250
251 ReadReturn r2;
252 r2.value = 11.0;
253 r2.updated = std::chrono::high_resolution_clock::now();
254 EXPECT_CALL(*sensor_ptr2, read()).WillOnce(Return(r2));
255
256 // Read the sensors, this will put the values into the cache.
257 zone->updateSensors();
258
259 // We should no longer be in failsafe mode.
260 EXPECT_FALSE(zone->getFailSafeMode());
261
262 EXPECT_EQ(r1.value, zone->getCachedValue(name1));
263 EXPECT_EQ(r2.value, zone->getCachedValue(name2));
264}
265
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700266TEST_F(PidZoneTest, FanInputTest_VerifiesFanValuesCached)
267{
Patrick Venturea58197c2018-06-11 15:29:45 -0700268 // This will add a couple fan inputs, and verify the values are cached.
269
270 std::string name1 = "fan1";
271 int64_t timeout = 2;
272
273 std::unique_ptr<Sensor> sensor1 =
274 std::make_unique<SensorMock>(name1, timeout);
Patrick Venturee2ec0f62018-09-04 12:30:27 -0700275 SensorMock* sensor_ptr1 = reinterpret_cast<SensorMock*>(sensor1.get());
Patrick Venturea58197c2018-06-11 15:29:45 -0700276
277 std::string name2 = "fan2";
278 std::unique_ptr<Sensor> sensor2 =
279 std::make_unique<SensorMock>(name2, timeout);
Patrick Venturee2ec0f62018-09-04 12:30:27 -0700280 SensorMock* sensor_ptr2 = reinterpret_cast<SensorMock*>(sensor2.get());
Patrick Venturea58197c2018-06-11 15:29:45 -0700281
282 std::string type = "unchecked";
283 mgr.addSensor(type, name1, std::move(sensor1));
284 EXPECT_EQ(mgr.getSensor(name1), sensor_ptr1);
285 mgr.addSensor(type, name2, std::move(sensor2));
286 EXPECT_EQ(mgr.getSensor(name2), sensor_ptr2);
287
288 // Now that the sensors exist, add them to the zone.
289 zone->addFanInput(name1);
290 zone->addFanInput(name2);
291
292 // Initialize Zone
293 zone->initializeCache();
294
295 ReadReturn r1;
296 r1.value = 10.0;
297 r1.updated = std::chrono::high_resolution_clock::now();
298 EXPECT_CALL(*sensor_ptr1, read()).WillOnce(Return(r1));
299
300 ReadReturn r2;
301 r2.value = 11.0;
302 r2.updated = std::chrono::high_resolution_clock::now();
303 EXPECT_CALL(*sensor_ptr2, read()).WillOnce(Return(r2));
304
305 // Method under test will read through each fan sensor for the zone and
306 // cache the values.
307 zone->updateFanTelemetry();
308
309 EXPECT_EQ(r1.value, zone->getCachedValue(name1));
310 EXPECT_EQ(r2.value, zone->getCachedValue(name2));
311}
312
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700313TEST_F(PidZoneTest, ThermalInput_ValueTimeoutEntersFailSafeMode)
314{
Patrick Venturea58197c2018-06-11 15:29:45 -0700315 // On the second updateSensors call, the updated timestamp will be beyond
316 // the timeout limit.
317
318 int64_t timeout = 1;
319
320 std::string name1 = "temp1";
321 std::unique_ptr<Sensor> sensor1 =
322 std::make_unique<SensorMock>(name1, timeout);
Patrick Venturee2ec0f62018-09-04 12:30:27 -0700323 SensorMock* sensor_ptr1 = reinterpret_cast<SensorMock*>(sensor1.get());
Patrick Venturea58197c2018-06-11 15:29:45 -0700324
325 std::string name2 = "temp2";
326 std::unique_ptr<Sensor> sensor2 =
327 std::make_unique<SensorMock>(name2, timeout);
Patrick Venturee2ec0f62018-09-04 12:30:27 -0700328 SensorMock* sensor_ptr2 = reinterpret_cast<SensorMock*>(sensor2.get());
Patrick Venturea58197c2018-06-11 15:29:45 -0700329
330 std::string type = "unchecked";
331 mgr.addSensor(type, name1, std::move(sensor1));
332 EXPECT_EQ(mgr.getSensor(name1), sensor_ptr1);
333 mgr.addSensor(type, name2, std::move(sensor2));
334 EXPECT_EQ(mgr.getSensor(name2), sensor_ptr2);
335
336 zone->addThermalInput(name1);
337 zone->addThermalInput(name2);
338
339 // Initialize Zone
340 zone->initializeCache();
341
342 // Verify now in failsafe mode.
343 EXPECT_TRUE(zone->getFailSafeMode());
344
345 ReadReturn r1;
346 r1.value = 10.0;
347 r1.updated = std::chrono::high_resolution_clock::now();
348 EXPECT_CALL(*sensor_ptr1, read()).WillOnce(Return(r1));
349
350 ReadReturn r2;
351 r2.value = 11.0;
352 r2.updated = std::chrono::high_resolution_clock::now();
353 EXPECT_CALL(*sensor_ptr2, read()).WillOnce(Return(r2));
354
355 zone->updateSensors();
356 EXPECT_FALSE(zone->getFailSafeMode());
357
358 // Ok, so we're not in failsafe mode, so let's set updated to the past.
359 // sensor1 will have an updated field older than its timeout value, but
360 // sensor2 will be fine. :D
361 r1.updated -= std::chrono::seconds(3);
362 r2.updated = std::chrono::high_resolution_clock::now();
363
364 EXPECT_CALL(*sensor_ptr1, read()).WillOnce(Return(r1));
365 EXPECT_CALL(*sensor_ptr2, read()).WillOnce(Return(r2));
366
367 // Method under test will read each sensor. One sensor's value is older
368 // than the timeout for that sensor and this triggers failsafe mode.
369 zone->updateSensors();
370 EXPECT_TRUE(zone->getFailSafeMode());
371}
372
Will Liangded0ab52019-05-15 17:10:06 +0800373TEST_F(PidZoneTest, FanInputTest_FailsafeToValid_ReadsSensors)
374{
375 // This will add a couple fan inputs, and verify the values are cached.
376
377 std::string name1 = "fan1";
378 int64_t timeout = 2;
379
380 std::unique_ptr<Sensor> sensor1 =
381 std::make_unique<SensorMock>(name1, timeout);
382 SensorMock* sensor_ptr1 = reinterpret_cast<SensorMock*>(sensor1.get());
383
384 std::string name2 = "fan2";
385 std::unique_ptr<Sensor> sensor2 =
386 std::make_unique<SensorMock>(name2, timeout);
387 SensorMock* sensor_ptr2 = reinterpret_cast<SensorMock*>(sensor2.get());
388
389 std::string type = "unchecked";
390 mgr.addSensor(type, name1, std::move(sensor1));
391 EXPECT_EQ(mgr.getSensor(name1), sensor_ptr1);
392 mgr.addSensor(type, name2, std::move(sensor2));
393 EXPECT_EQ(mgr.getSensor(name2), sensor_ptr2);
394
395 // Now that the sensors exist, add them to the zone.
396 zone->addFanInput(name1);
397 zone->addFanInput(name2);
398
399 // Initialize Zone
400 zone->initializeCache();
401
402 // Verify now in failsafe mode.
403 EXPECT_TRUE(zone->getFailSafeMode());
404
405 ReadReturn r1;
406 r1.value = 10.0;
407 r1.updated = std::chrono::high_resolution_clock::now();
408 EXPECT_CALL(*sensor_ptr1, read()).WillOnce(Return(r1));
409
410 ReadReturn r2;
411 r2.value = 11.0;
412 r2.updated = std::chrono::high_resolution_clock::now();
413 EXPECT_CALL(*sensor_ptr2, read()).WillOnce(Return(r2));
414
415 // Method under test will read through each fan sensor for the zone and
416 // cache the values.
417 zone->updateFanTelemetry();
418
419 // We should no longer be in failsafe mode.
420 EXPECT_FALSE(zone->getFailSafeMode());
421
422 EXPECT_EQ(r1.value, zone->getCachedValue(name1));
423 EXPECT_EQ(r2.value, zone->getCachedValue(name2));
424}
425
426TEST_F(PidZoneTest, FanInputTest_ValueTimeoutEntersFailSafeMode)
427{
428 // This will add a couple fan inputs, and verify the values are cached.
429
430 std::string name1 = "fan1";
431 int64_t timeout = 2;
432
433 std::unique_ptr<Sensor> sensor1 =
434 std::make_unique<SensorMock>(name1, timeout);
435 SensorMock* sensor_ptr1 = reinterpret_cast<SensorMock*>(sensor1.get());
436
437 std::string name2 = "fan2";
438 std::unique_ptr<Sensor> sensor2 =
439 std::make_unique<SensorMock>(name2, timeout);
440 SensorMock* sensor_ptr2 = reinterpret_cast<SensorMock*>(sensor2.get());
441
442 std::string type = "unchecked";
443 mgr.addSensor(type, name1, std::move(sensor1));
444 EXPECT_EQ(mgr.getSensor(name1), sensor_ptr1);
445 mgr.addSensor(type, name2, std::move(sensor2));
446 EXPECT_EQ(mgr.getSensor(name2), sensor_ptr2);
447
448 // Now that the sensors exist, add them to the zone.
449 zone->addFanInput(name1);
450 zone->addFanInput(name2);
451
452 // Initialize Zone
453 zone->initializeCache();
454
455 // Verify now in failsafe mode.
456 EXPECT_TRUE(zone->getFailSafeMode());
457
458 ReadReturn r1;
459 r1.value = 10.0;
460 r1.updated = std::chrono::high_resolution_clock::now();
461 EXPECT_CALL(*sensor_ptr1, read()).WillOnce(Return(r1));
462
463 ReadReturn r2;
464 r2.value = 11.0;
465 r2.updated = std::chrono::high_resolution_clock::now();
466 EXPECT_CALL(*sensor_ptr2, read()).WillOnce(Return(r2));
467
468 // Method under test will read through each fan sensor for the zone and
469 // cache the values.
470 zone->updateFanTelemetry();
471
472 // We should no longer be in failsafe mode.
473 EXPECT_FALSE(zone->getFailSafeMode());
474
475 r1.updated -= std::chrono::seconds(3);
476 r2.updated = std::chrono::high_resolution_clock::now();
477
478 EXPECT_CALL(*sensor_ptr1, read()).WillOnce(Return(r1));
479 EXPECT_CALL(*sensor_ptr2, read()).WillOnce(Return(r2));
480
481 zone->updateFanTelemetry();
482 EXPECT_TRUE(zone->getFailSafeMode());
483}
484
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700485TEST_F(PidZoneTest, GetSensorTest_ReturnsExpected)
486{
Patrick Venturea58197c2018-06-11 15:29:45 -0700487 // One can grab a sensor from the manager through the zone.
488
489 int64_t timeout = 1;
490
491 std::string name1 = "temp1";
492 std::unique_ptr<Sensor> sensor1 =
493 std::make_unique<SensorMock>(name1, timeout);
Patrick Venturee2ec0f62018-09-04 12:30:27 -0700494 SensorMock* sensor_ptr1 = reinterpret_cast<SensorMock*>(sensor1.get());
Patrick Venturea58197c2018-06-11 15:29:45 -0700495
496 std::string type = "unchecked";
497 mgr.addSensor(type, name1, std::move(sensor1));
498 EXPECT_EQ(mgr.getSensor(name1), sensor_ptr1);
499
500 zone->addThermalInput(name1);
501
502 // Verify method under test returns the pointer we expect.
503 EXPECT_EQ(mgr.getSensor(name1), zone->getSensor(name1));
504}
505
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700506TEST_F(PidZoneTest, AddThermalPIDTest_VerifiesThermalPIDsProcessed)
507{
Patrick Venturea58197c2018-06-11 15:29:45 -0700508 // Tests adding a thermal PID controller to the zone, and verifies it's
509 // touched during processing.
510
511 std::unique_ptr<PIDController> tpid =
512 std::make_unique<ControllerMock>("thermal1", zone.get());
Patrick Venturee2ec0f62018-09-04 12:30:27 -0700513 ControllerMock* tmock = reinterpret_cast<ControllerMock*>(tpid.get());
Patrick Venturea58197c2018-06-11 15:29:45 -0700514
515 // Access the internal pid configuration to clear it out (unrelated to the
516 // test).
Patrick Venture563a3562018-10-30 09:31:26 -0700517 ec::pid_info_t* info = tpid->getPIDInfo();
Patrick Venturea58197c2018-06-11 15:29:45 -0700518 std::memset(info, 0x00, sizeof(ec::pid_info_t));
519
520 zone->addThermalPID(std::move(tpid));
521
Patrick Venture563a3562018-10-30 09:31:26 -0700522 EXPECT_CALL(*tmock, setptProc()).WillOnce(Return(10.0));
523 EXPECT_CALL(*tmock, inputProc()).WillOnce(Return(11.0));
524 EXPECT_CALL(*tmock, outputProc(_));
Patrick Venturea58197c2018-06-11 15:29:45 -0700525
526 // Method under test will, for each thermal PID, call setpt, input, and
527 // output.
Patrick Venture563a3562018-10-30 09:31:26 -0700528 zone->processThermals();
Patrick Venturea58197c2018-06-11 15:29:45 -0700529}
530
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700531TEST_F(PidZoneTest, AddFanPIDTest_VerifiesFanPIDsProcessed)
532{
Patrick Venturea58197c2018-06-11 15:29:45 -0700533 // Tests adding a fan PID controller to the zone, and verifies it's
534 // touched during processing.
535
536 std::unique_ptr<PIDController> tpid =
537 std::make_unique<ControllerMock>("fan1", zone.get());
Patrick Venturee2ec0f62018-09-04 12:30:27 -0700538 ControllerMock* tmock = reinterpret_cast<ControllerMock*>(tpid.get());
Patrick Venturea58197c2018-06-11 15:29:45 -0700539
540 // Access the internal pid configuration to clear it out (unrelated to the
541 // test).
Patrick Venture563a3562018-10-30 09:31:26 -0700542 ec::pid_info_t* info = tpid->getPIDInfo();
Patrick Venturea58197c2018-06-11 15:29:45 -0700543 std::memset(info, 0x00, sizeof(ec::pid_info_t));
544
545 zone->addFanPID(std::move(tpid));
546
Patrick Venture563a3562018-10-30 09:31:26 -0700547 EXPECT_CALL(*tmock, setptProc()).WillOnce(Return(10.0));
548 EXPECT_CALL(*tmock, inputProc()).WillOnce(Return(11.0));
549 EXPECT_CALL(*tmock, outputProc(_));
Patrick Venturea58197c2018-06-11 15:29:45 -0700550
551 // Method under test will, for each fan PID, call setpt, input, and output.
Patrick Venture563a3562018-10-30 09:31:26 -0700552 zone->processFans();
Patrick Venturea58197c2018-06-11 15:29:45 -0700553}
554
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700555TEST_F(PidZoneTest, ManualModeDbusTest_VerifySetManualBehavesAsExpected)
556{
Patrick Venturea58197c2018-06-11 15:29:45 -0700557 // The manual(bool) method is inherited from the dbus mode interface.
558
559 // Verifies that someone doesn't remove the internal call to the dbus
560 // object from which we're inheriting.
561 EXPECT_CALL(sdbus_mock_mode,
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700562 sd_bus_emit_properties_changed_strv(
563 IsNull(), StrEq(objPath), StrEq(modeInterface), NotNull()))
Harvey.Wua1ae4fa2022-10-28 17:38:35 +0800564 .WillOnce(Invoke(
565 [&]([[maybe_unused]] sd_bus* bus, [[maybe_unused]] const char* path,
566 [[maybe_unused]] const char* interface, const char** names) {
567 EXPECT_STREQ("Manual", names[0]);
568 return 0;
569 }));
Patrick Venturea58197c2018-06-11 15:29:45 -0700570
571 // Method under test will set the manual mode to true and broadcast this
572 // change on dbus.
573 zone->manual(true);
574 EXPECT_TRUE(zone->getManualMode());
575}
576
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700577TEST_F(PidZoneTest, FailsafeDbusTest_VerifiesReturnsExpected)
578{
Patrick Venturea58197c2018-06-11 15:29:45 -0700579 // This property is implemented by us as read-only, such that trying to
580 // write to it will have no effect.
581 EXPECT_EQ(zone->failSafe(), zone->getFailSafeMode());
582}
Patrick Venturea0764872020-08-08 07:48:43 -0700583
584} // namespace
585} // namespace pid_control