blob: fee388c4973933fa59f78c53701921994a50835c [file] [log] [blame]
Ed Tanousf8b6e552025-06-27 13:27:50 -07001#include "conf.hpp"
James Zheng6df8bb52024-11-27 23:38:47 +00002#include "failsafeloggers/builder.hpp"
Ed Tanousf8b6e552025-06-27 13:27:50 -07003#include "interfaces.hpp"
Patrick Ventureda4a5dd2018-08-31 09:42:48 -07004#include "pid/ec/pid.hpp"
Ed Tanousf8b6e552025-06-27 13:27:50 -07005#include "pid/pidcontroller.hpp"
Patrick Venturea58197c2018-06-11 15:29:45 -07006#include "pid/zone.hpp"
Ed Tanousf8b6e552025-06-27 13:27:50 -07007#include "pid/zone_interface.hpp"
Patrick Ventureda4a5dd2018-08-31 09:42:48 -07008#include "sensors/manager.hpp"
Ed Tanousf8b6e552025-06-27 13:27:50 -07009#include "sensors/sensor.hpp"
Patrick Ventureda4a5dd2018-08-31 09:42:48 -070010#include "test/controller_mock.hpp"
11#include "test/helpers.hpp"
12#include "test/sensor_mock.hpp"
Patrick Venturea58197c2018-06-11 15:29:45 -070013
Ed Tanousf8b6e552025-06-27 13:27:50 -070014#include <systemd/sd-bus.h>
15
Patrick Venturea83a3ec2020-08-04 09:52:05 -070016#include <sdbusplus/test/sdbus_mock.hpp>
17
Patrick Venturea58197c2018-06-11 15:29:45 -070018#include <chrono>
Ed Tanousf8b6e552025-06-27 13:27:50 -070019#include <cstdint>
Patrick Venturea58197c2018-06-11 15:29:45 -070020#include <cstring>
Ed Tanousf8b6e552025-06-27 13:27:50 -070021#include <map>
22#include <memory>
Patrick Williamse3c60772025-04-07 17:53:42 -040023#include <optional>
Ed Tanousf8b6e552025-06-27 13:27:50 -070024#include <string>
James Zheng6df8bb52024-11-27 23:38:47 +000025#include <unordered_map>
Ed Tanousf8b6e552025-06-27 13:27:50 -070026#include <utility>
Patrick Venturea58197c2018-06-11 15:29:45 -070027#include <vector>
28
Patrick Ventureda4a5dd2018-08-31 09:42:48 -070029#include <gmock/gmock.h>
30#include <gtest/gtest.h>
Patrick Venturea58197c2018-06-11 15:29:45 -070031
Patrick Venturea0764872020-08-08 07:48:43 -070032namespace pid_control
33{
34namespace
35{
36
Patrick Ventureda4a5dd2018-08-31 09:42:48 -070037using ::testing::_;
Patrick Venturea58197c2018-06-11 15:29:45 -070038using ::testing::IsNull;
39using ::testing::Return;
40using ::testing::StrEq;
Patrick Venturea58197c2018-06-11 15:29:45 -070041
42static std::string modeInterface = "xyz.openbmc_project.Control.Mode";
Harvey Wucc0232a2023-02-09 14:58:55 +080043static std::string debugZoneInterface = "xyz.openbmc_project.Debug.Pid.Zone";
ykchiu7c6d35d2023-05-10 17:01:46 +080044static std::string enableInterface = "xyz.openbmc_project.Object.Enable";
Harvey Wu37180062023-10-02 09:42:50 +080045static std::string debugThermalPowerInterface =
46 "xyz.openbmc_project.Debug.Pid.ThermalPower";
Patrick Venturea58197c2018-06-11 15:29:45 -070047
Patrick Ventureda4a5dd2018-08-31 09:42:48 -070048namespace
49{
Patrick Venturea58197c2018-06-11 15:29:45 -070050
Patrick Ventureda4a5dd2018-08-31 09:42:48 -070051TEST(PidZoneConstructorTest, BoringConstructorTest)
52{
Patrick Venturea58197c2018-06-11 15:29:45 -070053 // Build a PID Zone.
54
ykchiu7c6d35d2023-05-10 17:01:46 +080055 sdbusplus::SdBusMock sdbus_mock_passive, sdbus_mock_host, sdbus_mock_mode,
56 sdbus_mock_enable;
Patrick Venturea58197c2018-06-11 15:29:45 -070057 auto bus_mock_passive = sdbusplus::get_mocked_new(&sdbus_mock_passive);
58 auto bus_mock_host = sdbusplus::get_mocked_new(&sdbus_mock_host);
59 auto bus_mock_mode = sdbusplus::get_mocked_new(&sdbus_mock_mode);
ykchiu7c6d35d2023-05-10 17:01:46 +080060 auto bus_mock_enable = sdbusplus::get_mocked_new(&sdbus_mock_enable);
Patrick Venturea58197c2018-06-11 15:29:45 -070061
62 EXPECT_CALL(sdbus_mock_host,
63 sd_bus_add_object_manager(
Patrick Ventureda4a5dd2018-08-31 09:42:48 -070064 IsNull(), _, StrEq("/xyz/openbmc_project/extsensors")))
Patrick Venturea58197c2018-06-11 15:29:45 -070065 .WillOnce(Return(0));
66
James Feist1fe08952019-05-07 09:17:16 -070067 SensorManager m(bus_mock_passive, bus_mock_host);
Patrick Venturea58197c2018-06-11 15:29:45 -070068
69 bool defer = true;
Delphine CC Chiu97889632023-11-06 11:32:46 +080070 bool accSetPoint = false;
Patrick Venturee2ec0f62018-09-04 12:30:27 -070071 const char* objPath = "/path/";
Patrick Venturea58197c2018-06-11 15:29:45 -070072 int64_t zone = 1;
James Feist3484bed2019-02-25 13:28:18 -080073 double minThermalOutput = 1000.0;
Harvey Wu92f9f3c2023-11-07 09:23:35 +080074 double failSafePercent = 100;
Bonnie Lo0e8fc392022-10-05 10:20:55 +080075 conf::CycleTime cycleTime;
Patrick Venturea58197c2018-06-11 15:29:45 -070076
James Feist0709e2f2020-07-08 10:59:45 -070077 double d;
Patrick Venturea58197c2018-06-11 15:29:45 -070078 std::vector<std::string> properties;
Patrick Ventureda4a5dd2018-08-31 09:42:48 -070079 SetupDbusObject(&sdbus_mock_mode, defer, objPath, modeInterface, properties,
James Feist0709e2f2020-07-08 10:59:45 -070080 &d);
Harvey Wucc0232a2023-02-09 14:58:55 +080081 SetupDbusObject(&sdbus_mock_mode, defer, objPath, debugZoneInterface,
82 properties, &d);
Patrick Venturea58197c2018-06-11 15:29:45 -070083
ykchiu7c6d35d2023-05-10 17:01:46 +080084 std::string sensorname = "temp1";
Patrick Williamsbd63bca2024-08-16 15:21:10 -040085 std::string pidsensorpath =
86 "/xyz/openbmc_project/settings/fanctrl/zone1/" + sensorname;
ykchiu7c6d35d2023-05-10 17:01:46 +080087
88 double de;
89 std::vector<std::string> propertiesenable;
90 SetupDbusObject(&sdbus_mock_enable, defer, pidsensorpath.c_str(),
91 enableInterface, propertiesenable, &de);
92
Bonnie Lo0e8fc392022-10-05 10:20:55 +080093 DbusPidZone p(zone, minThermalOutput, failSafePercent, cycleTime, m,
Delphine CC Chiu97889632023-11-06 11:32:46 +080094 bus_mock_mode, objPath, defer, accSetPoint);
Patrick Venturea58197c2018-06-11 15:29:45 -070095 // Success.
96}
97
Patrick Ventureda4a5dd2018-08-31 09:42:48 -070098} // namespace
Patrick Venturea58197c2018-06-11 15:29:45 -070099
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700100class PidZoneTest : public ::testing::Test
101{
102 protected:
103 PidZoneTest() :
Ed Tanousd2768c52025-06-26 11:42:57 -0700104 properties(), sdbus_mock_passive(), sdbus_mock_host(),
ykchiu7c6d35d2023-05-10 17:01:46 +0800105 sdbus_mock_mode(), sdbus_mock_enable()
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700106 {
107 EXPECT_CALL(sdbus_mock_host,
108 sd_bus_add_object_manager(
109 IsNull(), _, StrEq("/xyz/openbmc_project/extsensors")))
110 .WillOnce(Return(0));
Patrick Venturea58197c2018-06-11 15:29:45 -0700111
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700112 auto bus_mock_passive = sdbusplus::get_mocked_new(&sdbus_mock_passive);
113 auto bus_mock_host = sdbusplus::get_mocked_new(&sdbus_mock_host);
114 auto bus_mock_mode = sdbusplus::get_mocked_new(&sdbus_mock_mode);
ykchiu7c6d35d2023-05-10 17:01:46 +0800115 auto bus_mock_enable = sdbusplus::get_mocked_new(&sdbus_mock_enable);
Patrick Venturea58197c2018-06-11 15:29:45 -0700116
Patrick Williamse3c60772025-04-07 17:53:42 -0400117 mgr = SensorManager(bus_mock_passive, bus_mock_host);
Patrick Venturea58197c2018-06-11 15:29:45 -0700118
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700119 SetupDbusObject(&sdbus_mock_mode, defer, objPath, modeInterface,
120 properties, &property_index);
Harvey Wucc0232a2023-02-09 14:58:55 +0800121 SetupDbusObject(&sdbus_mock_mode, defer, objPath, debugZoneInterface,
122 properties, &property_index);
Patrick Venturea58197c2018-06-11 15:29:45 -0700123
ykchiu7c6d35d2023-05-10 17:01:46 +0800124 SetupDbusObject(&sdbus_mock_enable, defer, pidsensorpath.c_str(),
125 enableInterface, propertiesenable,
126 &propertyenable_index);
127
Delphine CC Chiu97889632023-11-06 11:32:46 +0800128 zone = std::make_unique<DbusPidZone>(
Patrick Williamse3c60772025-04-07 17:53:42 -0400129 zoneId, minThermalOutput, failSafePercent, cycleTime, *mgr,
Delphine CC Chiu97889632023-11-06 11:32:46 +0800130 bus_mock_mode, objPath, defer, accSetPoint);
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700131 }
Patrick Venturea58197c2018-06-11 15:29:45 -0700132
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700133 // unused
Ed Tanousd2768c52025-06-26 11:42:57 -0700134 double property_index{};
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700135 std::vector<std::string> properties;
ykchiu7c6d35d2023-05-10 17:01:46 +0800136 double propertyenable_index;
137 std::vector<std::string> propertiesenable;
Patrick Venturea58197c2018-06-11 15:29:45 -0700138
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700139 sdbusplus::SdBusMock sdbus_mock_passive;
140 sdbusplus::SdBusMock sdbus_mock_host;
141 sdbusplus::SdBusMock sdbus_mock_mode;
ykchiu7c6d35d2023-05-10 17:01:46 +0800142 sdbusplus::SdBusMock sdbus_mock_enable;
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700143 int64_t zoneId = 1;
James Feist3484bed2019-02-25 13:28:18 -0800144 double minThermalOutput = 1000.0;
Harvey Wu92f9f3c2023-11-07 09:23:35 +0800145 double failSafePercent = 100;
Harvey Wu37180062023-10-02 09:42:50 +0800146 double setpoint = 50.0;
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700147 bool defer = true;
Delphine CC Chiu97889632023-11-06 11:32:46 +0800148 bool accSetPoint = false;
Patrick Venturee2ec0f62018-09-04 12:30:27 -0700149 const char* objPath = "/path/";
Patrick Williamse3c60772025-04-07 17:53:42 -0400150 std::optional<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";
Patrick Williamsbd63bca2024-08-16 15:21:10 -0400155 std::string pidsensorpath =
156 "/xyz/openbmc_project/settings/fanctrl/zone1/" + sensorname;
ykchiu7c6d35d2023-05-10 17:01:46 +0800157
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) {
Patrick Williamsbd63bca2024-08-16 15:21:10 -0400191 EXPECT_STREQ("Enable", names[0]);
192 return 0;
193 }));
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).
Harvey Wu1b3b7302024-10-04 16:49:46 +0800211 [[maybe_unused]] ec::pid_info_t* info = tpid->getPIDInfo();
Josh Lehana4146eb2020-10-01 11:49:09 -0700212
213 zone->addFanPID(std::move(tpid));
214
215 EXPECT_CALL(*tmock, setptProc()).WillOnce(Return(10.0));
216 EXPECT_CALL(*tmock, inputProc()).WillOnce(Return(11.0));
217 EXPECT_CALL(*tmock, outputProc(_));
218
219 // while zone is in auto mode redundant writes should be disabled
220 EXPECT_FALSE(zone->getRedundantWrite());
221
222 // but switching from manual to auto enables a single redundant write
223 zone->setManualMode(true);
224 zone->setManualMode(false);
225 EXPECT_TRUE(zone->getRedundantWrite());
226
227 // after one iteration of a pid loop redundant write should be cleared
228 zone->processFans();
229 EXPECT_FALSE(zone->getRedundantWrite());
230}
231
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700232TEST_F(PidZoneTest, RpmSetPoints_AddMaxClear_BehaveAsExpected)
233{
Patrick Venturef7a2dd52019-07-16 14:31:13 -0700234 // Tests addSetPoint, clearSetPoints, determineMaxSetPointRequest
Nirav Shahccc8bb62022-02-17 21:06:51 -0800235 // and getMinThermalSetPoint.
Patrick Venturea58197c2018-06-11 15:29:45 -0700236
ykchiu7c6d35d2023-05-10 17:01:46 +0800237 // Need to add pid control process for the zone that can enable
238 // the process and add the set point.
239 auto bus_mock_enable = sdbusplus::get_mocked_new(&sdbus_mock_enable);
240
241 EXPECT_CALL(sdbus_mock_mode, sd_bus_emit_properties_changed_strv(
242 IsNull(), StrEq(pidsensorpath.c_str()),
243 StrEq(enableInterface), NotNull()))
244 .Times(::testing::AnyNumber())
245 .WillOnce(Invoke(
246 [&]([[maybe_unused]] sd_bus* bus, [[maybe_unused]] const char* path,
247 [[maybe_unused]] const char* interface, const char** names) {
Patrick Williamsbd63bca2024-08-16 15:21:10 -0400248 EXPECT_STREQ("Enable", names[0]);
249 return 0;
250 }));
ykchiu7c6d35d2023-05-10 17:01:46 +0800251
Harvey Wu37180062023-10-02 09:42:50 +0800252 zone->addPidControlProcess(sensorname, sensorType, setpoint,
253 bus_mock_enable, pidsensorpath.c_str(), defer);
ykchiu7c6d35d2023-05-10 17:01:46 +0800254
Patrick Venturea58197c2018-06-11 15:29:45 -0700255 // At least one value must be above the minimum thermal setpoint used in
256 // the constructor otherwise it'll choose that value
Patrick Venture5f59c0f2018-11-11 12:55:14 -0800257 std::vector<double> values = {100, 200, 300, 400, 500, 5000};
ykchiu7c6d35d2023-05-10 17:01:46 +0800258
Patrick Venturea58197c2018-06-11 15:29:45 -0700259 for (auto v : values)
260 {
ykchiu7c6d35d2023-05-10 17:01:46 +0800261 zone->addSetPoint(v, sensorname);
Patrick Venturea58197c2018-06-11 15:29:45 -0700262 }
263
264 // This will pull the maximum RPM setpoint request.
Patrick Venturef7a2dd52019-07-16 14:31:13 -0700265 zone->determineMaxSetPointRequest();
266 EXPECT_EQ(5000, zone->getMaxSetPointRequest());
Patrick Venturea58197c2018-06-11 15:29:45 -0700267
268 // Clear the values, so it'll choose the minimum thermal setpoint.
Patrick Venture9bbf3332019-07-16 10:50:37 -0700269 zone->clearSetPoints();
Patrick Venturea58197c2018-06-11 15:29:45 -0700270
271 // This will go through the RPM set point values and grab the maximum.
Patrick Venturef7a2dd52019-07-16 14:31:13 -0700272 zone->determineMaxSetPointRequest();
Nirav Shahccc8bb62022-02-17 21:06:51 -0800273 EXPECT_EQ(zone->getMinThermalSetPoint(), zone->getMaxSetPointRequest());
Patrick Venturea58197c2018-06-11 15:29:45 -0700274}
275
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700276TEST_F(PidZoneTest, RpmSetPoints_AddBelowMinimum_BehavesAsExpected)
277{
Patrick Venturea58197c2018-06-11 15:29:45 -0700278 // Tests adding several RPM setpoints, however, they're all lower than the
Patrick Venture7280e272019-02-11 10:45:32 -0800279 // configured minimal thermal setpoint RPM value.
Patrick Venturea58197c2018-06-11 15:29:45 -0700280
ykchiu7c6d35d2023-05-10 17:01:46 +0800281 // Need to add pid control process for the zone that can enable
282 // the process and add the set point.
283 auto bus_mock_enable = sdbusplus::get_mocked_new(&sdbus_mock_enable);
284
285 EXPECT_CALL(sdbus_mock_mode, sd_bus_emit_properties_changed_strv(
286 IsNull(), StrEq(pidsensorpath.c_str()),
287 StrEq(enableInterface), NotNull()))
288 .Times(::testing::AnyNumber())
289 .WillOnce(Invoke(
290 [&]([[maybe_unused]] sd_bus* bus, [[maybe_unused]] const char* path,
291 [[maybe_unused]] const char* interface, const char** names) {
Patrick Williamsbd63bca2024-08-16 15:21:10 -0400292 EXPECT_STREQ("Enable", names[0]);
293 return 0;
294 }));
ykchiu7c6d35d2023-05-10 17:01:46 +0800295
Harvey Wu37180062023-10-02 09:42:50 +0800296 zone->addPidControlProcess(sensorname, sensorType, setpoint,
297 bus_mock_enable, pidsensorpath.c_str(), defer);
ykchiu7c6d35d2023-05-10 17:01:46 +0800298
Patrick Venture5f59c0f2018-11-11 12:55:14 -0800299 std::vector<double> values = {100, 200, 300, 400, 500};
ykchiu7c6d35d2023-05-10 17:01:46 +0800300
Patrick Venturea58197c2018-06-11 15:29:45 -0700301 for (auto v : values)
302 {
ykchiu7c6d35d2023-05-10 17:01:46 +0800303 zone->addSetPoint(v, sensorname);
Patrick Venturea58197c2018-06-11 15:29:45 -0700304 }
305
306 // This will pull the maximum RPM setpoint request.
Patrick Venturef7a2dd52019-07-16 14:31:13 -0700307 zone->determineMaxSetPointRequest();
Patrick Venturea58197c2018-06-11 15:29:45 -0700308
309 // Verifies the value returned in the minimal thermal rpm set point.
Nirav Shahccc8bb62022-02-17 21:06:51 -0800310 EXPECT_EQ(zone->getMinThermalSetPoint(), zone->getMaxSetPointRequest());
Patrick Venturea58197c2018-06-11 15:29:45 -0700311}
312
Harvey Wu92f9f3c2023-11-07 09:23:35 +0800313TEST_F(PidZoneTest, GetFailSafePercent_SingleFailedReturnsExpected)
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700314{
Harvey Wu92f9f3c2023-11-07 09:23:35 +0800315 // Tests when only one sensor failed and the sensor's failsafe duty is zero,
316 // and verify that the sensor name is empty and failsafe duty is PID zone's
317 // failsafe duty.
ykchiu9fe3a3c2023-05-11 13:43:54 +0800318
Harvey Wu92f9f3c2023-11-07 09:23:35 +0800319 std::vector<std::string> input1 = {"temp1"};
320 std::vector<std::string> input2 = {"temp2"};
321 std::vector<std::string> input3 = {"temp3"};
ykchiu9fe3a3c2023-05-11 13:43:54 +0800322 std::vector<double> values = {0, 0, 0};
ykchiu9fe3a3c2023-05-11 13:43:54 +0800323
Harvey Wu92f9f3c2023-11-07 09:23:35 +0800324 zone->addPidFailSafePercent(input1, values[0]);
325 zone->addPidFailSafePercent(input2, values[1]);
326 zone->addPidFailSafePercent(input3, values[2]);
ykchiu9fe3a3c2023-05-11 13:43:54 +0800327
Harvey Wua4270072024-05-29 16:11:13 +0800328 zone->markSensorMissing("temp1", "Sensor threshold asserted");
ykchiu9fe3a3c2023-05-11 13:43:54 +0800329
Harvey Wu92f9f3c2023-11-07 09:23:35 +0800330 EXPECT_EQ(failSafePercent, zone->getFailSafePercent());
Harvey Wua4270072024-05-29 16:11:13 +0800331
332 std::map<std::string, std::pair<std::string, double>> failSensorList =
333 zone->getFailSafeSensors();
Ed Tanousd2768c52025-06-26 11:42:57 -0700334 EXPECT_EQ(1U, failSensorList.size());
Harvey Wua4270072024-05-29 16:11:13 +0800335 EXPECT_EQ("Sensor threshold asserted", failSensorList["temp1"].first);
336 EXPECT_EQ(failSafePercent, failSensorList["temp1"].second);
ykchiu9fe3a3c2023-05-11 13:43:54 +0800337}
338
Harvey Wu92f9f3c2023-11-07 09:23:35 +0800339TEST_F(PidZoneTest, GetFailSafePercent_MultiFailedReturnsExpected)
ykchiu9fe3a3c2023-05-11 13:43:54 +0800340{
Harvey Wu92f9f3c2023-11-07 09:23:35 +0800341 // Tests when multi sensor failed, and verify the final failsafe's sensor
342 // name and duty as expected.
ykchiu9fe3a3c2023-05-11 13:43:54 +0800343
Harvey Wu92f9f3c2023-11-07 09:23:35 +0800344 std::vector<std::string> input1 = {"temp1"};
345 std::vector<std::string> input2 = {"temp2"};
346 std::vector<std::string> input3 = {"temp3"};
ykchiu9fe3a3c2023-05-11 13:43:54 +0800347 std::vector<double> values = {60, 80, 70};
ykchiu9fe3a3c2023-05-11 13:43:54 +0800348
Harvey Wu92f9f3c2023-11-07 09:23:35 +0800349 zone->addPidFailSafePercent(input1, values[0]);
350 zone->addPidFailSafePercent(input2, values[1]);
351 zone->addPidFailSafePercent(input3, values[2]);
ykchiu9fe3a3c2023-05-11 13:43:54 +0800352
Harvey Wua4270072024-05-29 16:11:13 +0800353 zone->markSensorMissing("temp1", "Sensor threshold asserted");
354 zone->markSensorMissing("temp2", "Sensor reading bad");
355 zone->markSensorMissing("temp3", "Sensor unavailable");
ykchiu9fe3a3c2023-05-11 13:43:54 +0800356
Harvey Wu92f9f3c2023-11-07 09:23:35 +0800357 EXPECT_EQ(80, zone->getFailSafePercent());
Harvey Wua4270072024-05-29 16:11:13 +0800358
359 std::map<std::string, std::pair<std::string, double>> failSensorList =
360 zone->getFailSafeSensors();
Ed Tanousd2768c52025-06-26 11:42:57 -0700361 EXPECT_EQ(3U, failSensorList.size());
Harvey Wua4270072024-05-29 16:11:13 +0800362 EXPECT_EQ("Sensor threshold asserted", failSensorList["temp1"].first);
363 EXPECT_EQ(60, failSensorList["temp1"].second);
364 EXPECT_EQ("Sensor reading bad", failSensorList["temp2"].first);
365 EXPECT_EQ(80, failSensorList["temp2"].second);
366 EXPECT_EQ("Sensor unavailable", failSensorList["temp3"].first);
367 EXPECT_EQ(70, failSensorList["temp3"].second);
Patrick Venturea58197c2018-06-11 15:29:45 -0700368}
369
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700370TEST_F(PidZoneTest, ThermalInputs_FailsafeToValid_ReadsSensors)
371{
Patrick Venturea58197c2018-06-11 15:29:45 -0700372 // This test will add a couple thermal inputs, and verify that the zone
373 // initializes into failsafe mode, and will read each sensor.
374
James Zheng6df8bb52024-11-27 23:38:47 +0000375 // Disable failsafe logger for the unit test.
376 std::unordered_map<int64_t, std::shared_ptr<ZoneInterface>> empty_zone_map;
377 buildFailsafeLoggers(empty_zone_map, 0);
378
Patrick Venturea58197c2018-06-11 15:29:45 -0700379 std::string name1 = "temp1";
380 int64_t timeout = 1;
381
Patrick Williamsbd63bca2024-08-16 15:21:10 -0400382 std::unique_ptr<Sensor> sensor1 =
383 std::make_unique<SensorMock>(name1, timeout);
Patrick Venturee2ec0f62018-09-04 12:30:27 -0700384 SensorMock* sensor_ptr1 = reinterpret_cast<SensorMock*>(sensor1.get());
Patrick Venturea58197c2018-06-11 15:29:45 -0700385
386 std::string name2 = "temp2";
Patrick Williamsbd63bca2024-08-16 15:21:10 -0400387 std::unique_ptr<Sensor> sensor2 =
388 std::make_unique<SensorMock>(name2, timeout);
Patrick Venturee2ec0f62018-09-04 12:30:27 -0700389 SensorMock* sensor_ptr2 = reinterpret_cast<SensorMock*>(sensor2.get());
Patrick Venturea58197c2018-06-11 15:29:45 -0700390
391 std::string type = "unchecked";
Patrick Williamse3c60772025-04-07 17:53:42 -0400392 mgr->addSensor(type, name1, std::move(sensor1));
393 EXPECT_EQ(mgr->getSensor(name1), sensor_ptr1);
394 mgr->addSensor(type, name2, std::move(sensor2));
395 EXPECT_EQ(mgr->getSensor(name2), sensor_ptr2);
Patrick Venturea58197c2018-06-11 15:29:45 -0700396
397 // Now that the sensors exist, add them to the zone.
Josh Lehan3f0f7bc2023-02-13 01:45:29 -0800398 zone->addThermalInput(name1, false);
399 zone->addThermalInput(name2, false);
Patrick Venturea58197c2018-06-11 15:29:45 -0700400
401 // Initialize Zone
402 zone->initializeCache();
403
404 // Verify now in failsafe mode.
405 EXPECT_TRUE(zone->getFailSafeMode());
406
407 ReadReturn r1;
408 r1.value = 10.0;
409 r1.updated = std::chrono::high_resolution_clock::now();
410 EXPECT_CALL(*sensor_ptr1, read()).WillOnce(Return(r1));
411
412 ReadReturn r2;
413 r2.value = 11.0;
414 r2.updated = std::chrono::high_resolution_clock::now();
415 EXPECT_CALL(*sensor_ptr2, read()).WillOnce(Return(r2));
416
417 // Read the sensors, this will put the values into the cache.
418 zone->updateSensors();
419
420 // We should no longer be in failsafe mode.
421 EXPECT_FALSE(zone->getFailSafeMode());
422
423 EXPECT_EQ(r1.value, zone->getCachedValue(name1));
424 EXPECT_EQ(r2.value, zone->getCachedValue(name2));
425}
426
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700427TEST_F(PidZoneTest, FanInputTest_VerifiesFanValuesCached)
428{
Patrick Venturea58197c2018-06-11 15:29:45 -0700429 // This will add a couple fan inputs, and verify the values are cached.
430
James Zheng6df8bb52024-11-27 23:38:47 +0000431 // Disable failsafe logger for the unit test.
432 std::unordered_map<int64_t, std::shared_ptr<ZoneInterface>> empty_zone_map;
433 buildFailsafeLoggers(empty_zone_map, 0);
434
Patrick Venturea58197c2018-06-11 15:29:45 -0700435 std::string name1 = "fan1";
436 int64_t timeout = 2;
437
Patrick Williamsbd63bca2024-08-16 15:21:10 -0400438 std::unique_ptr<Sensor> sensor1 =
439 std::make_unique<SensorMock>(name1, timeout);
Patrick Venturee2ec0f62018-09-04 12:30:27 -0700440 SensorMock* sensor_ptr1 = reinterpret_cast<SensorMock*>(sensor1.get());
Patrick Venturea58197c2018-06-11 15:29:45 -0700441
442 std::string name2 = "fan2";
Patrick Williamsbd63bca2024-08-16 15:21:10 -0400443 std::unique_ptr<Sensor> sensor2 =
444 std::make_unique<SensorMock>(name2, timeout);
Patrick Venturee2ec0f62018-09-04 12:30:27 -0700445 SensorMock* sensor_ptr2 = reinterpret_cast<SensorMock*>(sensor2.get());
Patrick Venturea58197c2018-06-11 15:29:45 -0700446
447 std::string type = "unchecked";
Patrick Williamse3c60772025-04-07 17:53:42 -0400448 mgr->addSensor(type, name1, std::move(sensor1));
449 EXPECT_EQ(mgr->getSensor(name1), sensor_ptr1);
450 mgr->addSensor(type, name2, std::move(sensor2));
451 EXPECT_EQ(mgr->getSensor(name2), sensor_ptr2);
Patrick Venturea58197c2018-06-11 15:29:45 -0700452
453 // Now that the sensors exist, add them to the zone.
Josh Lehan3f0f7bc2023-02-13 01:45:29 -0800454 zone->addFanInput(name1, false);
455 zone->addFanInput(name2, false);
Patrick Venturea58197c2018-06-11 15:29:45 -0700456
457 // Initialize Zone
458 zone->initializeCache();
459
460 ReadReturn r1;
461 r1.value = 10.0;
462 r1.updated = std::chrono::high_resolution_clock::now();
463 EXPECT_CALL(*sensor_ptr1, read()).WillOnce(Return(r1));
464
465 ReadReturn r2;
466 r2.value = 11.0;
467 r2.updated = std::chrono::high_resolution_clock::now();
468 EXPECT_CALL(*sensor_ptr2, read()).WillOnce(Return(r2));
469
470 // Method under test will read through each fan sensor for the zone and
471 // cache the values.
472 zone->updateFanTelemetry();
473
474 EXPECT_EQ(r1.value, zone->getCachedValue(name1));
475 EXPECT_EQ(r2.value, zone->getCachedValue(name2));
476}
477
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700478TEST_F(PidZoneTest, ThermalInput_ValueTimeoutEntersFailSafeMode)
479{
Patrick Venturea58197c2018-06-11 15:29:45 -0700480 // On the second updateSensors call, the updated timestamp will be beyond
481 // the timeout limit.
482
James Zheng6df8bb52024-11-27 23:38:47 +0000483 // Disable failsafe logger for the unit test.
484 std::unordered_map<int64_t, std::shared_ptr<ZoneInterface>> empty_zone_map;
485 buildFailsafeLoggers(empty_zone_map, 0);
486
Patrick Venturea58197c2018-06-11 15:29:45 -0700487 int64_t timeout = 1;
488
489 std::string name1 = "temp1";
Patrick Williamsbd63bca2024-08-16 15:21:10 -0400490 std::unique_ptr<Sensor> sensor1 =
491 std::make_unique<SensorMock>(name1, timeout);
Patrick Venturee2ec0f62018-09-04 12:30:27 -0700492 SensorMock* sensor_ptr1 = reinterpret_cast<SensorMock*>(sensor1.get());
Patrick Venturea58197c2018-06-11 15:29:45 -0700493
494 std::string name2 = "temp2";
Patrick Williamsbd63bca2024-08-16 15:21:10 -0400495 std::unique_ptr<Sensor> sensor2 =
496 std::make_unique<SensorMock>(name2, timeout);
Patrick Venturee2ec0f62018-09-04 12:30:27 -0700497 SensorMock* sensor_ptr2 = reinterpret_cast<SensorMock*>(sensor2.get());
Patrick Venturea58197c2018-06-11 15:29:45 -0700498
499 std::string type = "unchecked";
Patrick Williamse3c60772025-04-07 17:53:42 -0400500 mgr->addSensor(type, name1, std::move(sensor1));
501 EXPECT_EQ(mgr->getSensor(name1), sensor_ptr1);
502 mgr->addSensor(type, name2, std::move(sensor2));
503 EXPECT_EQ(mgr->getSensor(name2), sensor_ptr2);
Patrick Venturea58197c2018-06-11 15:29:45 -0700504
Josh Lehan3f0f7bc2023-02-13 01:45:29 -0800505 zone->addThermalInput(name1, false);
506 zone->addThermalInput(name2, false);
Patrick Venturea58197c2018-06-11 15:29:45 -0700507
508 // Initialize Zone
509 zone->initializeCache();
510
511 // Verify now in failsafe mode.
512 EXPECT_TRUE(zone->getFailSafeMode());
513
514 ReadReturn r1;
515 r1.value = 10.0;
516 r1.updated = std::chrono::high_resolution_clock::now();
517 EXPECT_CALL(*sensor_ptr1, read()).WillOnce(Return(r1));
518
519 ReadReturn r2;
520 r2.value = 11.0;
521 r2.updated = std::chrono::high_resolution_clock::now();
522 EXPECT_CALL(*sensor_ptr2, read()).WillOnce(Return(r2));
523
524 zone->updateSensors();
525 EXPECT_FALSE(zone->getFailSafeMode());
526
527 // Ok, so we're not in failsafe mode, so let's set updated to the past.
528 // sensor1 will have an updated field older than its timeout value, but
529 // sensor2 will be fine. :D
530 r1.updated -= std::chrono::seconds(3);
531 r2.updated = std::chrono::high_resolution_clock::now();
532
533 EXPECT_CALL(*sensor_ptr1, read()).WillOnce(Return(r1));
534 EXPECT_CALL(*sensor_ptr2, read()).WillOnce(Return(r2));
535
536 // Method under test will read each sensor. One sensor's value is older
537 // than the timeout for that sensor and this triggers failsafe mode.
538 zone->updateSensors();
539 EXPECT_TRUE(zone->getFailSafeMode());
540}
541
Josh Lehan3f0f7bc2023-02-13 01:45:29 -0800542TEST_F(PidZoneTest, ThermalInput_MissingIsAcceptableNoFailSafe)
543{
544 // This is similar to the above test, but because missingIsAcceptable
545 // is set for sensor1, the zone should not enter failsafe mode when
546 // only sensor1 goes missing.
547 // However, sensor2 going missing should still trigger failsafe mode.
548
James Zheng6df8bb52024-11-27 23:38:47 +0000549 // Disable failsafe logger for the unit test.
550 std::unordered_map<int64_t, std::shared_ptr<ZoneInterface>> empty_zone_map;
551 buildFailsafeLoggers(empty_zone_map, 0);
552
Josh Lehan3f0f7bc2023-02-13 01:45:29 -0800553 int64_t timeout = 1;
554
555 std::string name1 = "temp1";
Patrick Williamsbd63bca2024-08-16 15:21:10 -0400556 std::unique_ptr<Sensor> sensor1 =
557 std::make_unique<SensorMock>(name1, timeout);
Josh Lehan3f0f7bc2023-02-13 01:45:29 -0800558 SensorMock* sensor_ptr1 = reinterpret_cast<SensorMock*>(sensor1.get());
559
560 std::string name2 = "temp2";
Patrick Williamsbd63bca2024-08-16 15:21:10 -0400561 std::unique_ptr<Sensor> sensor2 =
562 std::make_unique<SensorMock>(name2, timeout);
Josh Lehan3f0f7bc2023-02-13 01:45:29 -0800563 SensorMock* sensor_ptr2 = reinterpret_cast<SensorMock*>(sensor2.get());
564
565 std::string type = "unchecked";
Patrick Williamse3c60772025-04-07 17:53:42 -0400566 mgr->addSensor(type, name1, std::move(sensor1));
567 EXPECT_EQ(mgr->getSensor(name1), sensor_ptr1);
568 mgr->addSensor(type, name2, std::move(sensor2));
569 EXPECT_EQ(mgr->getSensor(name2), sensor_ptr2);
Josh Lehan3f0f7bc2023-02-13 01:45:29 -0800570
571 // Only sensor1 has MissingIsAcceptable enabled for it
572 zone->addThermalInput(name1, true);
573 zone->addThermalInput(name2, false);
574
575 // Initialize Zone
576 zone->initializeCache();
577
578 // As sensors are not initialized, zone should be in failsafe mode
579 EXPECT_TRUE(zone->getFailSafeMode());
580
581 // r1 not populated here, intentionally, to simulate a sensor that
582 // is not available yet, perhaps takes a long time to start up.
583 ReadReturn r1;
584 EXPECT_CALL(*sensor_ptr1, read()).WillOnce(Return(r1));
585
586 ReadReturn r2;
587 r2.value = 11.0;
588 r2.updated = std::chrono::high_resolution_clock::now();
589 EXPECT_CALL(*sensor_ptr2, read()).WillOnce(Return(r2));
590
591 zone->updateSensors();
592
593 // Only sensor2 has been initialized here. Failsafe should be false,
594 // because sensor1 MissingIsAcceptable so it is OK for it to go missing.
595 EXPECT_FALSE(zone->getFailSafeMode());
596
597 r1.value = 10.0;
598 r1.updated = std::chrono::high_resolution_clock::now();
599
600 EXPECT_CALL(*sensor_ptr1, read()).WillOnce(Return(r1));
601 EXPECT_CALL(*sensor_ptr2, read()).WillOnce(Return(r2));
602 zone->updateSensors();
603
604 // Both sensors are now properly initialized
605 EXPECT_FALSE(zone->getFailSafeMode());
606
607 // Ok, so we're not in failsafe mode, so let's set updated to the past.
608 // sensor1 will have an updated field older than its timeout value, but
609 // sensor2 will be fine. :D
610 r1.updated -= std::chrono::seconds(3);
611 r2.updated = std::chrono::high_resolution_clock::now();
612
613 EXPECT_CALL(*sensor_ptr1, read()).WillOnce(Return(r1));
614 EXPECT_CALL(*sensor_ptr2, read()).WillOnce(Return(r2));
615 zone->updateSensors();
616
617 // MissingIsAcceptable is true for sensor1, so the zone should not be
618 // thrown into failsafe mode.
619 EXPECT_FALSE(zone->getFailSafeMode());
620
621 // Do the same thing, but for the opposite sensors: r1 is good,
622 // but r2 is set to some time in the past.
623 r1.updated = std::chrono::high_resolution_clock::now();
624 r2.updated -= std::chrono::seconds(3);
625
626 EXPECT_CALL(*sensor_ptr1, read()).WillOnce(Return(r1));
627 EXPECT_CALL(*sensor_ptr2, read()).WillOnce(Return(r2));
628 zone->updateSensors();
629
630 // Now, the zone should be in failsafe mode, because sensor2 does not
631 // have MissingIsAcceptable set true, it is still subject to failsafe.
632 EXPECT_TRUE(zone->getFailSafeMode());
633
634 r1.updated = std::chrono::high_resolution_clock::now();
635 r2.updated = std::chrono::high_resolution_clock::now();
636
637 EXPECT_CALL(*sensor_ptr1, read()).WillOnce(Return(r1));
638 EXPECT_CALL(*sensor_ptr2, read()).WillOnce(Return(r2));
639 zone->updateSensors();
640
641 // The failsafe mode should cease, as both sensors are good again.
642 EXPECT_FALSE(zone->getFailSafeMode());
643}
644
Will Liangded0ab52019-05-15 17:10:06 +0800645TEST_F(PidZoneTest, FanInputTest_FailsafeToValid_ReadsSensors)
646{
647 // This will add a couple fan inputs, and verify the values are cached.
648
James Zheng6df8bb52024-11-27 23:38:47 +0000649 // Disable failsafe logger for the unit test.
650 std::unordered_map<int64_t, std::shared_ptr<ZoneInterface>> empty_zone_map;
651 buildFailsafeLoggers(empty_zone_map, 0);
652
Will Liangded0ab52019-05-15 17:10:06 +0800653 std::string name1 = "fan1";
654 int64_t timeout = 2;
655
Patrick Williamsbd63bca2024-08-16 15:21:10 -0400656 std::unique_ptr<Sensor> sensor1 =
657 std::make_unique<SensorMock>(name1, timeout);
Will Liangded0ab52019-05-15 17:10:06 +0800658 SensorMock* sensor_ptr1 = reinterpret_cast<SensorMock*>(sensor1.get());
659
660 std::string name2 = "fan2";
Patrick Williamsbd63bca2024-08-16 15:21:10 -0400661 std::unique_ptr<Sensor> sensor2 =
662 std::make_unique<SensorMock>(name2, timeout);
Will Liangded0ab52019-05-15 17:10:06 +0800663 SensorMock* sensor_ptr2 = reinterpret_cast<SensorMock*>(sensor2.get());
664
665 std::string type = "unchecked";
Patrick Williamse3c60772025-04-07 17:53:42 -0400666 mgr->addSensor(type, name1, std::move(sensor1));
667 EXPECT_EQ(mgr->getSensor(name1), sensor_ptr1);
668 mgr->addSensor(type, name2, std::move(sensor2));
669 EXPECT_EQ(mgr->getSensor(name2), sensor_ptr2);
Will Liangded0ab52019-05-15 17:10:06 +0800670
671 // Now that the sensors exist, add them to the zone.
Josh Lehan3f0f7bc2023-02-13 01:45:29 -0800672 zone->addFanInput(name1, false);
673 zone->addFanInput(name2, false);
Will Liangded0ab52019-05-15 17:10:06 +0800674
675 // Initialize Zone
676 zone->initializeCache();
677
678 // Verify now in failsafe mode.
679 EXPECT_TRUE(zone->getFailSafeMode());
680
681 ReadReturn r1;
682 r1.value = 10.0;
683 r1.updated = std::chrono::high_resolution_clock::now();
684 EXPECT_CALL(*sensor_ptr1, read()).WillOnce(Return(r1));
685
686 ReadReturn r2;
687 r2.value = 11.0;
688 r2.updated = std::chrono::high_resolution_clock::now();
689 EXPECT_CALL(*sensor_ptr2, read()).WillOnce(Return(r2));
690
691 // Method under test will read through each fan sensor for the zone and
692 // cache the values.
693 zone->updateFanTelemetry();
694
695 // We should no longer be in failsafe mode.
696 EXPECT_FALSE(zone->getFailSafeMode());
697
698 EXPECT_EQ(r1.value, zone->getCachedValue(name1));
699 EXPECT_EQ(r2.value, zone->getCachedValue(name2));
700}
701
702TEST_F(PidZoneTest, FanInputTest_ValueTimeoutEntersFailSafeMode)
703{
704 // This will add a couple fan inputs, and verify the values are cached.
705
James Zheng6df8bb52024-11-27 23:38:47 +0000706 // Disable failsafe logger for the unit test.
707 std::unordered_map<int64_t, std::shared_ptr<ZoneInterface>> empty_zone_map;
708 buildFailsafeLoggers(empty_zone_map, 0);
709
Will Liangded0ab52019-05-15 17:10:06 +0800710 std::string name1 = "fan1";
711 int64_t timeout = 2;
712
Patrick Williamsbd63bca2024-08-16 15:21:10 -0400713 std::unique_ptr<Sensor> sensor1 =
714 std::make_unique<SensorMock>(name1, timeout);
Will Liangded0ab52019-05-15 17:10:06 +0800715 SensorMock* sensor_ptr1 = reinterpret_cast<SensorMock*>(sensor1.get());
716
717 std::string name2 = "fan2";
Patrick Williamsbd63bca2024-08-16 15:21:10 -0400718 std::unique_ptr<Sensor> sensor2 =
719 std::make_unique<SensorMock>(name2, timeout);
Will Liangded0ab52019-05-15 17:10:06 +0800720 SensorMock* sensor_ptr2 = reinterpret_cast<SensorMock*>(sensor2.get());
721
722 std::string type = "unchecked";
Patrick Williamse3c60772025-04-07 17:53:42 -0400723 mgr->addSensor(type, name1, std::move(sensor1));
724 EXPECT_EQ(mgr->getSensor(name1), sensor_ptr1);
725 mgr->addSensor(type, name2, std::move(sensor2));
726 EXPECT_EQ(mgr->getSensor(name2), sensor_ptr2);
Will Liangded0ab52019-05-15 17:10:06 +0800727
728 // Now that the sensors exist, add them to the zone.
Josh Lehan3f0f7bc2023-02-13 01:45:29 -0800729 zone->addFanInput(name1, false);
730 zone->addFanInput(name2, false);
Will Liangded0ab52019-05-15 17:10:06 +0800731
732 // Initialize Zone
733 zone->initializeCache();
734
735 // Verify now in failsafe mode.
736 EXPECT_TRUE(zone->getFailSafeMode());
737
738 ReadReturn r1;
739 r1.value = 10.0;
740 r1.updated = std::chrono::high_resolution_clock::now();
741 EXPECT_CALL(*sensor_ptr1, read()).WillOnce(Return(r1));
742
743 ReadReturn r2;
744 r2.value = 11.0;
745 r2.updated = std::chrono::high_resolution_clock::now();
746 EXPECT_CALL(*sensor_ptr2, read()).WillOnce(Return(r2));
747
748 // Method under test will read through each fan sensor for the zone and
749 // cache the values.
750 zone->updateFanTelemetry();
751
752 // We should no longer be in failsafe mode.
753 EXPECT_FALSE(zone->getFailSafeMode());
754
755 r1.updated -= std::chrono::seconds(3);
756 r2.updated = std::chrono::high_resolution_clock::now();
757
758 EXPECT_CALL(*sensor_ptr1, read()).WillOnce(Return(r1));
759 EXPECT_CALL(*sensor_ptr2, read()).WillOnce(Return(r2));
760
761 zone->updateFanTelemetry();
762 EXPECT_TRUE(zone->getFailSafeMode());
763}
764
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700765TEST_F(PidZoneTest, GetSensorTest_ReturnsExpected)
766{
Patrick Venturea58197c2018-06-11 15:29:45 -0700767 // One can grab a sensor from the manager through the zone.
768
James Zheng6df8bb52024-11-27 23:38:47 +0000769 // Disable failsafe logger for the unit test.
770 std::unordered_map<int64_t, std::shared_ptr<ZoneInterface>> empty_zone_map;
771 buildFailsafeLoggers(empty_zone_map, 0);
772
Patrick Venturea58197c2018-06-11 15:29:45 -0700773 int64_t timeout = 1;
774
775 std::string name1 = "temp1";
Patrick Williamsbd63bca2024-08-16 15:21:10 -0400776 std::unique_ptr<Sensor> sensor1 =
777 std::make_unique<SensorMock>(name1, timeout);
Patrick Venturee2ec0f62018-09-04 12:30:27 -0700778 SensorMock* sensor_ptr1 = reinterpret_cast<SensorMock*>(sensor1.get());
Patrick Venturea58197c2018-06-11 15:29:45 -0700779
780 std::string type = "unchecked";
Patrick Williamse3c60772025-04-07 17:53:42 -0400781 mgr->addSensor(type, name1, std::move(sensor1));
782 EXPECT_EQ(mgr->getSensor(name1), sensor_ptr1);
Patrick Venturea58197c2018-06-11 15:29:45 -0700783
Josh Lehan3f0f7bc2023-02-13 01:45:29 -0800784 zone->addThermalInput(name1, false);
Patrick Venturea58197c2018-06-11 15:29:45 -0700785
786 // Verify method under test returns the pointer we expect.
Patrick Williamse3c60772025-04-07 17:53:42 -0400787 EXPECT_EQ(mgr->getSensor(name1), zone->getSensor(name1));
Patrick Venturea58197c2018-06-11 15:29:45 -0700788}
789
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700790TEST_F(PidZoneTest, AddThermalPIDTest_VerifiesThermalPIDsProcessed)
791{
Patrick Venturea58197c2018-06-11 15:29:45 -0700792 // Tests adding a thermal PID controller to the zone, and verifies it's
793 // touched during processing.
794
795 std::unique_ptr<PIDController> tpid =
796 std::make_unique<ControllerMock>("thermal1", zone.get());
Patrick Venturee2ec0f62018-09-04 12:30:27 -0700797 ControllerMock* tmock = reinterpret_cast<ControllerMock*>(tpid.get());
Patrick Venturea58197c2018-06-11 15:29:45 -0700798
799 // Access the internal pid configuration to clear it out (unrelated to the
800 // test).
Harvey Wu1b3b7302024-10-04 16:49:46 +0800801 [[maybe_unused]] ec::pid_info_t* info = tpid->getPIDInfo();
Patrick Venturea58197c2018-06-11 15:29:45 -0700802
803 zone->addThermalPID(std::move(tpid));
804
Patrick Venture563a3562018-10-30 09:31:26 -0700805 EXPECT_CALL(*tmock, setptProc()).WillOnce(Return(10.0));
806 EXPECT_CALL(*tmock, inputProc()).WillOnce(Return(11.0));
807 EXPECT_CALL(*tmock, outputProc(_));
Patrick Venturea58197c2018-06-11 15:29:45 -0700808
809 // Method under test will, for each thermal PID, call setpt, input, and
810 // output.
Patrick Venture563a3562018-10-30 09:31:26 -0700811 zone->processThermals();
Patrick Venturea58197c2018-06-11 15:29:45 -0700812}
813
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700814TEST_F(PidZoneTest, AddFanPIDTest_VerifiesFanPIDsProcessed)
815{
Patrick Venturea58197c2018-06-11 15:29:45 -0700816 // Tests adding a fan PID controller to the zone, and verifies it's
817 // touched during processing.
818
819 std::unique_ptr<PIDController> tpid =
820 std::make_unique<ControllerMock>("fan1", zone.get());
Patrick Venturee2ec0f62018-09-04 12:30:27 -0700821 ControllerMock* tmock = reinterpret_cast<ControllerMock*>(tpid.get());
Patrick Venturea58197c2018-06-11 15:29:45 -0700822
823 // Access the internal pid configuration to clear it out (unrelated to the
824 // test).
Harvey Wu1b3b7302024-10-04 16:49:46 +0800825 [[maybe_unused]] ec::pid_info_t* info = tpid->getPIDInfo();
Patrick Venturea58197c2018-06-11 15:29:45 -0700826
827 zone->addFanPID(std::move(tpid));
828
Patrick Venture563a3562018-10-30 09:31:26 -0700829 EXPECT_CALL(*tmock, setptProc()).WillOnce(Return(10.0));
830 EXPECT_CALL(*tmock, inputProc()).WillOnce(Return(11.0));
831 EXPECT_CALL(*tmock, outputProc(_));
Patrick Venturea58197c2018-06-11 15:29:45 -0700832
833 // Method under test will, for each fan PID, call setpt, input, and output.
Patrick Venture563a3562018-10-30 09:31:26 -0700834 zone->processFans();
Patrick Venturea58197c2018-06-11 15:29:45 -0700835}
836
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700837TEST_F(PidZoneTest, ManualModeDbusTest_VerifySetManualBehavesAsExpected)
838{
Patrick Venturea58197c2018-06-11 15:29:45 -0700839 // The manual(bool) method is inherited from the dbus mode interface.
840
841 // Verifies that someone doesn't remove the internal call to the dbus
842 // object from which we're inheriting.
843 EXPECT_CALL(sdbus_mock_mode,
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700844 sd_bus_emit_properties_changed_strv(
845 IsNull(), StrEq(objPath), StrEq(modeInterface), NotNull()))
Harvey.Wua1ae4fa2022-10-28 17:38:35 +0800846 .WillOnce(Invoke(
847 [&]([[maybe_unused]] sd_bus* bus, [[maybe_unused]] const char* path,
848 [[maybe_unused]] const char* interface, const char** names) {
Patrick Williamsbd63bca2024-08-16 15:21:10 -0400849 EXPECT_STREQ("Manual", names[0]);
850 return 0;
851 }));
Patrick Venturea58197c2018-06-11 15:29:45 -0700852
853 // Method under test will set the manual mode to true and broadcast this
854 // change on dbus.
855 zone->manual(true);
856 EXPECT_TRUE(zone->getManualMode());
857}
858
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700859TEST_F(PidZoneTest, FailsafeDbusTest_VerifiesReturnsExpected)
860{
Patrick Venturea58197c2018-06-11 15:29:45 -0700861 // This property is implemented by us as read-only, such that trying to
862 // write to it will have no effect.
James Zheng6df8bb52024-11-27 23:38:47 +0000863
864 // Disable failsafe logger for the unit test.
865 std::unordered_map<int64_t, std::shared_ptr<ZoneInterface>> empty_zone_map;
866 buildFailsafeLoggers(empty_zone_map, 0);
867
Patrick Venturea58197c2018-06-11 15:29:45 -0700868 EXPECT_EQ(zone->failSafe(), zone->getFailSafeMode());
869}
Patrick Venturea0764872020-08-08 07:48:43 -0700870
871} // namespace
872} // namespace pid_control