blob: 2c0e147a53350a31afde6b8bd08f7477d9267255 [file] [log] [blame]
Patrick Ventureda4a5dd2018-08-31 09:42:48 -07001#include "pid/ec/pid.hpp"
Patrick Venturea58197c2018-06-11 15:29:45 -07002#include "pid/zone.hpp"
Patrick Ventureda4a5dd2018-08-31 09:42:48 -07003#include "sensors/manager.hpp"
4#include "test/controller_mock.hpp"
5#include "test/helpers.hpp"
6#include "test/sensor_mock.hpp"
Patrick Venturea58197c2018-06-11 15:29:45 -07007
Patrick Venturea83a3ec2020-08-04 09:52:05 -07008#include <sdbusplus/test/sdbus_mock.hpp>
9
Patrick Venturea58197c2018-06-11 15:29:45 -070010#include <chrono>
11#include <cstring>
Patrick Venturea58197c2018-06-11 15:29:45 -070012#include <vector>
13
Patrick Ventureda4a5dd2018-08-31 09:42:48 -070014#include <gmock/gmock.h>
15#include <gtest/gtest.h>
Patrick Venturea58197c2018-06-11 15:29:45 -070016
Patrick Venturea0764872020-08-08 07:48:43 -070017namespace pid_control
18{
19namespace
20{
21
Patrick Ventureda4a5dd2018-08-31 09:42:48 -070022using ::testing::_;
Patrick Venturea58197c2018-06-11 15:29:45 -070023using ::testing::IsNull;
24using ::testing::Return;
25using ::testing::StrEq;
Patrick Venturea58197c2018-06-11 15:29:45 -070026
27static std::string modeInterface = "xyz.openbmc_project.Control.Mode";
28
Patrick Ventureda4a5dd2018-08-31 09:42:48 -070029namespace
30{
Patrick Venturea58197c2018-06-11 15:29:45 -070031
Patrick Ventureda4a5dd2018-08-31 09:42:48 -070032TEST(PidZoneConstructorTest, BoringConstructorTest)
33{
Patrick Venturea58197c2018-06-11 15:29:45 -070034 // Build a PID Zone.
35
36 sdbusplus::SdBusMock sdbus_mock_passive, sdbus_mock_host, sdbus_mock_mode;
37 auto bus_mock_passive = sdbusplus::get_mocked_new(&sdbus_mock_passive);
38 auto bus_mock_host = sdbusplus::get_mocked_new(&sdbus_mock_host);
39 auto bus_mock_mode = sdbusplus::get_mocked_new(&sdbus_mock_mode);
40
41 EXPECT_CALL(sdbus_mock_host,
42 sd_bus_add_object_manager(
Patrick Ventureda4a5dd2018-08-31 09:42:48 -070043 IsNull(), _, StrEq("/xyz/openbmc_project/extsensors")))
Patrick Venturea58197c2018-06-11 15:29:45 -070044 .WillOnce(Return(0));
45
James Feist1fe08952019-05-07 09:17:16 -070046 SensorManager m(bus_mock_passive, bus_mock_host);
Patrick Venturea58197c2018-06-11 15:29:45 -070047
48 bool defer = true;
Patrick Venturee2ec0f62018-09-04 12:30:27 -070049 const char* objPath = "/path/";
Patrick Venturea58197c2018-06-11 15:29:45 -070050 int64_t zone = 1;
James Feist3484bed2019-02-25 13:28:18 -080051 double minThermalOutput = 1000.0;
Patrick Venture5f59c0f2018-11-11 12:55:14 -080052 double failSafePercent = 0.75;
Bonnie Lo0e8fc392022-10-05 10:20:55 +080053 conf::CycleTime cycleTime;
Patrick Venturea58197c2018-06-11 15:29:45 -070054
James Feist0709e2f2020-07-08 10:59:45 -070055 double d;
Patrick Venturea58197c2018-06-11 15:29:45 -070056 std::vector<std::string> properties;
Patrick Ventureda4a5dd2018-08-31 09:42:48 -070057 SetupDbusObject(&sdbus_mock_mode, defer, objPath, modeInterface, properties,
James Feist0709e2f2020-07-08 10:59:45 -070058 &d);
Patrick Venturea58197c2018-06-11 15:29:45 -070059
Bonnie Lo0e8fc392022-10-05 10:20:55 +080060 DbusPidZone p(zone, minThermalOutput, failSafePercent, cycleTime, m,
61 bus_mock_mode, objPath, defer);
Patrick Venturea58197c2018-06-11 15:29:45 -070062 // Success.
63}
64
Patrick Ventureda4a5dd2018-08-31 09:42:48 -070065} // namespace
Patrick Venturea58197c2018-06-11 15:29:45 -070066
Patrick Ventureda4a5dd2018-08-31 09:42:48 -070067class PidZoneTest : public ::testing::Test
68{
69 protected:
70 PidZoneTest() :
71 property_index(), properties(), sdbus_mock_passive(), sdbus_mock_host(),
72 sdbus_mock_mode()
73 {
74 EXPECT_CALL(sdbus_mock_host,
75 sd_bus_add_object_manager(
76 IsNull(), _, StrEq("/xyz/openbmc_project/extsensors")))
77 .WillOnce(Return(0));
Patrick Venturea58197c2018-06-11 15:29:45 -070078
Patrick Ventureda4a5dd2018-08-31 09:42:48 -070079 auto bus_mock_passive = sdbusplus::get_mocked_new(&sdbus_mock_passive);
80 auto bus_mock_host = sdbusplus::get_mocked_new(&sdbus_mock_host);
81 auto bus_mock_mode = sdbusplus::get_mocked_new(&sdbus_mock_mode);
Patrick Venturea58197c2018-06-11 15:29:45 -070082
Patrick Ventureda4a5dd2018-08-31 09:42:48 -070083 // Compiler weirdly not happy about just instantiating mgr(...);
James Feist1fe08952019-05-07 09:17:16 -070084 SensorManager m(bus_mock_passive, bus_mock_host);
Patrick Ventureda4a5dd2018-08-31 09:42:48 -070085 mgr = std::move(m);
Patrick Venturea58197c2018-06-11 15:29:45 -070086
Patrick Ventureda4a5dd2018-08-31 09:42:48 -070087 SetupDbusObject(&sdbus_mock_mode, defer, objPath, modeInterface,
88 properties, &property_index);
Patrick Venturea58197c2018-06-11 15:29:45 -070089
Patrick Venture597ebd62020-08-11 08:48:19 -070090 zone = std::make_unique<DbusPidZone>(zoneId, minThermalOutput,
Bonnie Lo0e8fc392022-10-05 10:20:55 +080091 failSafePercent, cycleTime, mgr,
Patrick Venture597ebd62020-08-11 08:48:19 -070092 bus_mock_mode, objPath, defer);
Patrick Ventureda4a5dd2018-08-31 09:42:48 -070093 }
Patrick Venturea58197c2018-06-11 15:29:45 -070094
Patrick Ventureda4a5dd2018-08-31 09:42:48 -070095 // unused
James Feist0709e2f2020-07-08 10:59:45 -070096 double property_index;
Patrick Ventureda4a5dd2018-08-31 09:42:48 -070097 std::vector<std::string> properties;
Patrick Venturea58197c2018-06-11 15:29:45 -070098
Patrick Ventureda4a5dd2018-08-31 09:42:48 -070099 sdbusplus::SdBusMock sdbus_mock_passive;
100 sdbusplus::SdBusMock sdbus_mock_host;
101 sdbusplus::SdBusMock sdbus_mock_mode;
102 int64_t zoneId = 1;
James Feist3484bed2019-02-25 13:28:18 -0800103 double minThermalOutput = 1000.0;
Patrick Venture5f59c0f2018-11-11 12:55:14 -0800104 double failSafePercent = 0.75;
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700105 bool defer = true;
Patrick Venturee2ec0f62018-09-04 12:30:27 -0700106 const char* objPath = "/path/";
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700107 SensorManager mgr;
Bonnie Lo0e8fc392022-10-05 10:20:55 +0800108 conf::CycleTime cycleTime;
Patrick Venturea58197c2018-06-11 15:29:45 -0700109
Patrick Venture597ebd62020-08-11 08:48:19 -0700110 std::unique_ptr<DbusPidZone> zone;
Patrick Venturea58197c2018-06-11 15:29:45 -0700111};
112
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700113TEST_F(PidZoneTest, GetZoneId_ReturnsExpected)
114{
Patrick Venturea58197c2018-06-11 15:29:45 -0700115 // Verifies the zoneId returned is what we expect.
116
Patrick Venture0bbeaf82018-10-30 18:50:31 -0700117 EXPECT_EQ(zoneId, zone->getZoneID());
Patrick Venturea58197c2018-06-11 15:29:45 -0700118}
119
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700120TEST_F(PidZoneTest, GetAndSetManualModeTest_BehavesAsExpected)
121{
Patrick Venturea58197c2018-06-11 15:29:45 -0700122 // Verifies that the zone starts in manual mode. Verifies that one can set
123 // the mode.
124 EXPECT_FALSE(zone->getManualMode());
125
126 zone->setManualMode(true);
127 EXPECT_TRUE(zone->getManualMode());
128}
129
Josh Lehana4146eb2020-10-01 11:49:09 -0700130TEST_F(PidZoneTest, SetManualMode_RedundantWritesEnabledOnceAfterManualMode)
131{
132 // Tests adding a fan PID controller to the zone, and verifies it's
133 // touched during processing.
134
135 std::unique_ptr<PIDController> tpid =
136 std::make_unique<ControllerMock>("fan1", zone.get());
137 ControllerMock* tmock = reinterpret_cast<ControllerMock*>(tpid.get());
138
139 // Access the internal pid configuration to clear it out (unrelated to the
140 // test).
141 ec::pid_info_t* info = tpid->getPIDInfo();
142 std::memset(info, 0x00, sizeof(ec::pid_info_t));
143
144 zone->addFanPID(std::move(tpid));
145
146 EXPECT_CALL(*tmock, setptProc()).WillOnce(Return(10.0));
147 EXPECT_CALL(*tmock, inputProc()).WillOnce(Return(11.0));
148 EXPECT_CALL(*tmock, outputProc(_));
149
150 // while zone is in auto mode redundant writes should be disabled
151 EXPECT_FALSE(zone->getRedundantWrite());
152
153 // but switching from manual to auto enables a single redundant write
154 zone->setManualMode(true);
155 zone->setManualMode(false);
156 EXPECT_TRUE(zone->getRedundantWrite());
157
158 // after one iteration of a pid loop redundant write should be cleared
159 zone->processFans();
160 EXPECT_FALSE(zone->getRedundantWrite());
161}
162
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700163TEST_F(PidZoneTest, RpmSetPoints_AddMaxClear_BehaveAsExpected)
164{
Patrick Venturef7a2dd52019-07-16 14:31:13 -0700165 // Tests addSetPoint, clearSetPoints, determineMaxSetPointRequest
Nirav Shahccc8bb62022-02-17 21:06:51 -0800166 // and getMinThermalSetPoint.
Patrick Venturea58197c2018-06-11 15:29:45 -0700167
168 // At least one value must be above the minimum thermal setpoint used in
169 // the constructor otherwise it'll choose that value
Patrick Venture5f59c0f2018-11-11 12:55:14 -0800170 std::vector<double> values = {100, 200, 300, 400, 500, 5000};
Patrick Venturea58197c2018-06-11 15:29:45 -0700171 for (auto v : values)
172 {
Nirav Shahccc8bb62022-02-17 21:06:51 -0800173 zone->addSetPoint(v, "");
Patrick Venturea58197c2018-06-11 15:29:45 -0700174 }
175
176 // This will pull the maximum RPM setpoint request.
Patrick Venturef7a2dd52019-07-16 14:31:13 -0700177 zone->determineMaxSetPointRequest();
178 EXPECT_EQ(5000, zone->getMaxSetPointRequest());
Patrick Venturea58197c2018-06-11 15:29:45 -0700179
180 // Clear the values, so it'll choose the minimum thermal setpoint.
Patrick Venture9bbf3332019-07-16 10:50:37 -0700181 zone->clearSetPoints();
Patrick Venturea58197c2018-06-11 15:29:45 -0700182
183 // This will go through the RPM set point values and grab the maximum.
Patrick Venturef7a2dd52019-07-16 14:31:13 -0700184 zone->determineMaxSetPointRequest();
Nirav Shahccc8bb62022-02-17 21:06:51 -0800185 EXPECT_EQ(zone->getMinThermalSetPoint(), zone->getMaxSetPointRequest());
Patrick Venturea58197c2018-06-11 15:29:45 -0700186}
187
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700188TEST_F(PidZoneTest, RpmSetPoints_AddBelowMinimum_BehavesAsExpected)
189{
Patrick Venturea58197c2018-06-11 15:29:45 -0700190 // Tests adding several RPM setpoints, however, they're all lower than the
Patrick Venture7280e272019-02-11 10:45:32 -0800191 // configured minimal thermal setpoint RPM value.
Patrick Venturea58197c2018-06-11 15:29:45 -0700192
Patrick Venture5f59c0f2018-11-11 12:55:14 -0800193 std::vector<double> values = {100, 200, 300, 400, 500};
Patrick Venturea58197c2018-06-11 15:29:45 -0700194 for (auto v : values)
195 {
Nirav Shahccc8bb62022-02-17 21:06:51 -0800196 zone->addSetPoint(v, "");
Patrick Venturea58197c2018-06-11 15:29:45 -0700197 }
198
199 // This will pull the maximum RPM setpoint request.
Patrick Venturef7a2dd52019-07-16 14:31:13 -0700200 zone->determineMaxSetPointRequest();
Patrick Venturea58197c2018-06-11 15:29:45 -0700201
202 // Verifies the value returned in the minimal thermal rpm set point.
Nirav Shahccc8bb62022-02-17 21:06:51 -0800203 EXPECT_EQ(zone->getMinThermalSetPoint(), zone->getMaxSetPointRequest());
Patrick Venturea58197c2018-06-11 15:29:45 -0700204}
205
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700206TEST_F(PidZoneTest, GetFailSafePercent_ReturnsExpected)
207{
Patrick Venturea58197c2018-06-11 15:29:45 -0700208 // Verify the value used to create the object is stored.
209 EXPECT_EQ(failSafePercent, zone->getFailSafePercent());
210}
211
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700212TEST_F(PidZoneTest, ThermalInputs_FailsafeToValid_ReadsSensors)
213{
Patrick Venturea58197c2018-06-11 15:29:45 -0700214 // This test will add a couple thermal inputs, and verify that the zone
215 // initializes into failsafe mode, and will read each sensor.
216
217 std::string name1 = "temp1";
218 int64_t timeout = 1;
219
220 std::unique_ptr<Sensor> sensor1 =
221 std::make_unique<SensorMock>(name1, timeout);
Patrick Venturee2ec0f62018-09-04 12:30:27 -0700222 SensorMock* sensor_ptr1 = reinterpret_cast<SensorMock*>(sensor1.get());
Patrick Venturea58197c2018-06-11 15:29:45 -0700223
224 std::string name2 = "temp2";
225 std::unique_ptr<Sensor> sensor2 =
226 std::make_unique<SensorMock>(name2, timeout);
Patrick Venturee2ec0f62018-09-04 12:30:27 -0700227 SensorMock* sensor_ptr2 = reinterpret_cast<SensorMock*>(sensor2.get());
Patrick Venturea58197c2018-06-11 15:29:45 -0700228
229 std::string type = "unchecked";
230 mgr.addSensor(type, name1, std::move(sensor1));
231 EXPECT_EQ(mgr.getSensor(name1), sensor_ptr1);
232 mgr.addSensor(type, name2, std::move(sensor2));
233 EXPECT_EQ(mgr.getSensor(name2), sensor_ptr2);
234
235 // Now that the sensors exist, add them to the zone.
236 zone->addThermalInput(name1);
237 zone->addThermalInput(name2);
238
239 // Initialize Zone
240 zone->initializeCache();
241
242 // Verify now in failsafe mode.
243 EXPECT_TRUE(zone->getFailSafeMode());
244
245 ReadReturn r1;
246 r1.value = 10.0;
247 r1.updated = std::chrono::high_resolution_clock::now();
248 EXPECT_CALL(*sensor_ptr1, read()).WillOnce(Return(r1));
249
250 ReadReturn r2;
251 r2.value = 11.0;
252 r2.updated = std::chrono::high_resolution_clock::now();
253 EXPECT_CALL(*sensor_ptr2, read()).WillOnce(Return(r2));
254
255 // Read the sensors, this will put the values into the cache.
256 zone->updateSensors();
257
258 // We should no longer be in failsafe mode.
259 EXPECT_FALSE(zone->getFailSafeMode());
260
261 EXPECT_EQ(r1.value, zone->getCachedValue(name1));
262 EXPECT_EQ(r2.value, zone->getCachedValue(name2));
263}
264
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700265TEST_F(PidZoneTest, FanInputTest_VerifiesFanValuesCached)
266{
Patrick Venturea58197c2018-06-11 15:29:45 -0700267 // This will add a couple fan inputs, and verify the values are cached.
268
269 std::string name1 = "fan1";
270 int64_t timeout = 2;
271
272 std::unique_ptr<Sensor> sensor1 =
273 std::make_unique<SensorMock>(name1, timeout);
Patrick Venturee2ec0f62018-09-04 12:30:27 -0700274 SensorMock* sensor_ptr1 = reinterpret_cast<SensorMock*>(sensor1.get());
Patrick Venturea58197c2018-06-11 15:29:45 -0700275
276 std::string name2 = "fan2";
277 std::unique_ptr<Sensor> sensor2 =
278 std::make_unique<SensorMock>(name2, timeout);
Patrick Venturee2ec0f62018-09-04 12:30:27 -0700279 SensorMock* sensor_ptr2 = reinterpret_cast<SensorMock*>(sensor2.get());
Patrick Venturea58197c2018-06-11 15:29:45 -0700280
281 std::string type = "unchecked";
282 mgr.addSensor(type, name1, std::move(sensor1));
283 EXPECT_EQ(mgr.getSensor(name1), sensor_ptr1);
284 mgr.addSensor(type, name2, std::move(sensor2));
285 EXPECT_EQ(mgr.getSensor(name2), sensor_ptr2);
286
287 // Now that the sensors exist, add them to the zone.
288 zone->addFanInput(name1);
289 zone->addFanInput(name2);
290
291 // Initialize Zone
292 zone->initializeCache();
293
294 ReadReturn r1;
295 r1.value = 10.0;
296 r1.updated = std::chrono::high_resolution_clock::now();
297 EXPECT_CALL(*sensor_ptr1, read()).WillOnce(Return(r1));
298
299 ReadReturn r2;
300 r2.value = 11.0;
301 r2.updated = std::chrono::high_resolution_clock::now();
302 EXPECT_CALL(*sensor_ptr2, read()).WillOnce(Return(r2));
303
304 // Method under test will read through each fan sensor for the zone and
305 // cache the values.
306 zone->updateFanTelemetry();
307
308 EXPECT_EQ(r1.value, zone->getCachedValue(name1));
309 EXPECT_EQ(r2.value, zone->getCachedValue(name2));
310}
311
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700312TEST_F(PidZoneTest, ThermalInput_ValueTimeoutEntersFailSafeMode)
313{
Patrick Venturea58197c2018-06-11 15:29:45 -0700314 // On the second updateSensors call, the updated timestamp will be beyond
315 // the timeout limit.
316
317 int64_t timeout = 1;
318
319 std::string name1 = "temp1";
320 std::unique_ptr<Sensor> sensor1 =
321 std::make_unique<SensorMock>(name1, timeout);
Patrick Venturee2ec0f62018-09-04 12:30:27 -0700322 SensorMock* sensor_ptr1 = reinterpret_cast<SensorMock*>(sensor1.get());
Patrick Venturea58197c2018-06-11 15:29:45 -0700323
324 std::string name2 = "temp2";
325 std::unique_ptr<Sensor> sensor2 =
326 std::make_unique<SensorMock>(name2, timeout);
Patrick Venturee2ec0f62018-09-04 12:30:27 -0700327 SensorMock* sensor_ptr2 = reinterpret_cast<SensorMock*>(sensor2.get());
Patrick Venturea58197c2018-06-11 15:29:45 -0700328
329 std::string type = "unchecked";
330 mgr.addSensor(type, name1, std::move(sensor1));
331 EXPECT_EQ(mgr.getSensor(name1), sensor_ptr1);
332 mgr.addSensor(type, name2, std::move(sensor2));
333 EXPECT_EQ(mgr.getSensor(name2), sensor_ptr2);
334
335 zone->addThermalInput(name1);
336 zone->addThermalInput(name2);
337
338 // Initialize Zone
339 zone->initializeCache();
340
341 // Verify now in failsafe mode.
342 EXPECT_TRUE(zone->getFailSafeMode());
343
344 ReadReturn r1;
345 r1.value = 10.0;
346 r1.updated = std::chrono::high_resolution_clock::now();
347 EXPECT_CALL(*sensor_ptr1, read()).WillOnce(Return(r1));
348
349 ReadReturn r2;
350 r2.value = 11.0;
351 r2.updated = std::chrono::high_resolution_clock::now();
352 EXPECT_CALL(*sensor_ptr2, read()).WillOnce(Return(r2));
353
354 zone->updateSensors();
355 EXPECT_FALSE(zone->getFailSafeMode());
356
357 // Ok, so we're not in failsafe mode, so let's set updated to the past.
358 // sensor1 will have an updated field older than its timeout value, but
359 // sensor2 will be fine. :D
360 r1.updated -= std::chrono::seconds(3);
361 r2.updated = std::chrono::high_resolution_clock::now();
362
363 EXPECT_CALL(*sensor_ptr1, read()).WillOnce(Return(r1));
364 EXPECT_CALL(*sensor_ptr2, read()).WillOnce(Return(r2));
365
366 // Method under test will read each sensor. One sensor's value is older
367 // than the timeout for that sensor and this triggers failsafe mode.
368 zone->updateSensors();
369 EXPECT_TRUE(zone->getFailSafeMode());
370}
371
Will Liangded0ab52019-05-15 17:10:06 +0800372TEST_F(PidZoneTest, FanInputTest_FailsafeToValid_ReadsSensors)
373{
374 // This will add a couple fan inputs, and verify the values are cached.
375
376 std::string name1 = "fan1";
377 int64_t timeout = 2;
378
379 std::unique_ptr<Sensor> sensor1 =
380 std::make_unique<SensorMock>(name1, timeout);
381 SensorMock* sensor_ptr1 = reinterpret_cast<SensorMock*>(sensor1.get());
382
383 std::string name2 = "fan2";
384 std::unique_ptr<Sensor> sensor2 =
385 std::make_unique<SensorMock>(name2, timeout);
386 SensorMock* sensor_ptr2 = reinterpret_cast<SensorMock*>(sensor2.get());
387
388 std::string type = "unchecked";
389 mgr.addSensor(type, name1, std::move(sensor1));
390 EXPECT_EQ(mgr.getSensor(name1), sensor_ptr1);
391 mgr.addSensor(type, name2, std::move(sensor2));
392 EXPECT_EQ(mgr.getSensor(name2), sensor_ptr2);
393
394 // Now that the sensors exist, add them to the zone.
395 zone->addFanInput(name1);
396 zone->addFanInput(name2);
397
398 // Initialize Zone
399 zone->initializeCache();
400
401 // Verify now in failsafe mode.
402 EXPECT_TRUE(zone->getFailSafeMode());
403
404 ReadReturn r1;
405 r1.value = 10.0;
406 r1.updated = std::chrono::high_resolution_clock::now();
407 EXPECT_CALL(*sensor_ptr1, read()).WillOnce(Return(r1));
408
409 ReadReturn r2;
410 r2.value = 11.0;
411 r2.updated = std::chrono::high_resolution_clock::now();
412 EXPECT_CALL(*sensor_ptr2, read()).WillOnce(Return(r2));
413
414 // Method under test will read through each fan sensor for the zone and
415 // cache the values.
416 zone->updateFanTelemetry();
417
418 // We should no longer be in failsafe mode.
419 EXPECT_FALSE(zone->getFailSafeMode());
420
421 EXPECT_EQ(r1.value, zone->getCachedValue(name1));
422 EXPECT_EQ(r2.value, zone->getCachedValue(name2));
423}
424
425TEST_F(PidZoneTest, FanInputTest_ValueTimeoutEntersFailSafeMode)
426{
427 // This will add a couple fan inputs, and verify the values are cached.
428
429 std::string name1 = "fan1";
430 int64_t timeout = 2;
431
432 std::unique_ptr<Sensor> sensor1 =
433 std::make_unique<SensorMock>(name1, timeout);
434 SensorMock* sensor_ptr1 = reinterpret_cast<SensorMock*>(sensor1.get());
435
436 std::string name2 = "fan2";
437 std::unique_ptr<Sensor> sensor2 =
438 std::make_unique<SensorMock>(name2, timeout);
439 SensorMock* sensor_ptr2 = reinterpret_cast<SensorMock*>(sensor2.get());
440
441 std::string type = "unchecked";
442 mgr.addSensor(type, name1, std::move(sensor1));
443 EXPECT_EQ(mgr.getSensor(name1), sensor_ptr1);
444 mgr.addSensor(type, name2, std::move(sensor2));
445 EXPECT_EQ(mgr.getSensor(name2), sensor_ptr2);
446
447 // Now that the sensors exist, add them to the zone.
448 zone->addFanInput(name1);
449 zone->addFanInput(name2);
450
451 // Initialize Zone
452 zone->initializeCache();
453
454 // Verify now in failsafe mode.
455 EXPECT_TRUE(zone->getFailSafeMode());
456
457 ReadReturn r1;
458 r1.value = 10.0;
459 r1.updated = std::chrono::high_resolution_clock::now();
460 EXPECT_CALL(*sensor_ptr1, read()).WillOnce(Return(r1));
461
462 ReadReturn r2;
463 r2.value = 11.0;
464 r2.updated = std::chrono::high_resolution_clock::now();
465 EXPECT_CALL(*sensor_ptr2, read()).WillOnce(Return(r2));
466
467 // Method under test will read through each fan sensor for the zone and
468 // cache the values.
469 zone->updateFanTelemetry();
470
471 // We should no longer be in failsafe mode.
472 EXPECT_FALSE(zone->getFailSafeMode());
473
474 r1.updated -= std::chrono::seconds(3);
475 r2.updated = std::chrono::high_resolution_clock::now();
476
477 EXPECT_CALL(*sensor_ptr1, read()).WillOnce(Return(r1));
478 EXPECT_CALL(*sensor_ptr2, read()).WillOnce(Return(r2));
479
480 zone->updateFanTelemetry();
481 EXPECT_TRUE(zone->getFailSafeMode());
482}
483
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700484TEST_F(PidZoneTest, GetSensorTest_ReturnsExpected)
485{
Patrick Venturea58197c2018-06-11 15:29:45 -0700486 // One can grab a sensor from the manager through the zone.
487
488 int64_t timeout = 1;
489
490 std::string name1 = "temp1";
491 std::unique_ptr<Sensor> sensor1 =
492 std::make_unique<SensorMock>(name1, timeout);
Patrick Venturee2ec0f62018-09-04 12:30:27 -0700493 SensorMock* sensor_ptr1 = reinterpret_cast<SensorMock*>(sensor1.get());
Patrick Venturea58197c2018-06-11 15:29:45 -0700494
495 std::string type = "unchecked";
496 mgr.addSensor(type, name1, std::move(sensor1));
497 EXPECT_EQ(mgr.getSensor(name1), sensor_ptr1);
498
499 zone->addThermalInput(name1);
500
501 // Verify method under test returns the pointer we expect.
502 EXPECT_EQ(mgr.getSensor(name1), zone->getSensor(name1));
503}
504
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700505TEST_F(PidZoneTest, AddThermalPIDTest_VerifiesThermalPIDsProcessed)
506{
Patrick Venturea58197c2018-06-11 15:29:45 -0700507 // Tests adding a thermal PID controller to the zone, and verifies it's
508 // touched during processing.
509
510 std::unique_ptr<PIDController> tpid =
511 std::make_unique<ControllerMock>("thermal1", zone.get());
Patrick Venturee2ec0f62018-09-04 12:30:27 -0700512 ControllerMock* tmock = reinterpret_cast<ControllerMock*>(tpid.get());
Patrick Venturea58197c2018-06-11 15:29:45 -0700513
514 // Access the internal pid configuration to clear it out (unrelated to the
515 // test).
Patrick Venture563a3562018-10-30 09:31:26 -0700516 ec::pid_info_t* info = tpid->getPIDInfo();
Patrick Venturea58197c2018-06-11 15:29:45 -0700517 std::memset(info, 0x00, sizeof(ec::pid_info_t));
518
519 zone->addThermalPID(std::move(tpid));
520
Patrick Venture563a3562018-10-30 09:31:26 -0700521 EXPECT_CALL(*tmock, setptProc()).WillOnce(Return(10.0));
522 EXPECT_CALL(*tmock, inputProc()).WillOnce(Return(11.0));
523 EXPECT_CALL(*tmock, outputProc(_));
Patrick Venturea58197c2018-06-11 15:29:45 -0700524
525 // Method under test will, for each thermal PID, call setpt, input, and
526 // output.
Patrick Venture563a3562018-10-30 09:31:26 -0700527 zone->processThermals();
Patrick Venturea58197c2018-06-11 15:29:45 -0700528}
529
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700530TEST_F(PidZoneTest, AddFanPIDTest_VerifiesFanPIDsProcessed)
531{
Patrick Venturea58197c2018-06-11 15:29:45 -0700532 // Tests adding a fan PID controller to the zone, and verifies it's
533 // touched during processing.
534
535 std::unique_ptr<PIDController> tpid =
536 std::make_unique<ControllerMock>("fan1", zone.get());
Patrick Venturee2ec0f62018-09-04 12:30:27 -0700537 ControllerMock* tmock = reinterpret_cast<ControllerMock*>(tpid.get());
Patrick Venturea58197c2018-06-11 15:29:45 -0700538
539 // Access the internal pid configuration to clear it out (unrelated to the
540 // test).
Patrick Venture563a3562018-10-30 09:31:26 -0700541 ec::pid_info_t* info = tpid->getPIDInfo();
Patrick Venturea58197c2018-06-11 15:29:45 -0700542 std::memset(info, 0x00, sizeof(ec::pid_info_t));
543
544 zone->addFanPID(std::move(tpid));
545
Patrick Venture563a3562018-10-30 09:31:26 -0700546 EXPECT_CALL(*tmock, setptProc()).WillOnce(Return(10.0));
547 EXPECT_CALL(*tmock, inputProc()).WillOnce(Return(11.0));
548 EXPECT_CALL(*tmock, outputProc(_));
Patrick Venturea58197c2018-06-11 15:29:45 -0700549
550 // Method under test will, for each fan PID, call setpt, input, and output.
Patrick Venture563a3562018-10-30 09:31:26 -0700551 zone->processFans();
Patrick Venturea58197c2018-06-11 15:29:45 -0700552}
553
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700554TEST_F(PidZoneTest, ManualModeDbusTest_VerifySetManualBehavesAsExpected)
555{
Patrick Venturea58197c2018-06-11 15:29:45 -0700556 // The manual(bool) method is inherited from the dbus mode interface.
557
558 // Verifies that someone doesn't remove the internal call to the dbus
559 // object from which we're inheriting.
560 EXPECT_CALL(sdbus_mock_mode,
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700561 sd_bus_emit_properties_changed_strv(
562 IsNull(), StrEq(objPath), StrEq(modeInterface), NotNull()))
Harvey.Wua1ae4fa2022-10-28 17:38:35 +0800563 .WillOnce(Invoke(
564 [&]([[maybe_unused]] sd_bus* bus, [[maybe_unused]] const char* path,
565 [[maybe_unused]] const char* interface, const char** names) {
566 EXPECT_STREQ("Manual", names[0]);
567 return 0;
568 }));
Patrick Venturea58197c2018-06-11 15:29:45 -0700569
570 // Method under test will set the manual mode to true and broadcast this
571 // change on dbus.
572 zone->manual(true);
573 EXPECT_TRUE(zone->getManualMode());
574}
575
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700576TEST_F(PidZoneTest, FailsafeDbusTest_VerifiesReturnsExpected)
577{
Patrick Venturea58197c2018-06-11 15:29:45 -0700578 // This property is implemented by us as read-only, such that trying to
579 // write to it will have no effect.
580 EXPECT_EQ(zone->failSafe(), zone->getFailSafeMode());
581}
Patrick Venturea0764872020-08-08 07:48:43 -0700582
583} // namespace
584} // namespace pid_control