blob: c511b3c594535c923b9d2e3b2820ea50cb3985da [file] [log] [blame]
Patrick Venturea58197c2018-06-11 15:29:45 -07001#include "pid/zone.hpp"
2
3#include <chrono>
4#include <cstring>
5#include <gmock/gmock.h>
6#include <gtest/gtest.h>
7#include <sdbusplus/test/sdbus_mock.hpp>
8#include <vector>
9
10#include "pid/ec/pid.hpp"
11#include "sensors/manager.hpp"
12#include "test/controller_mock.hpp"
13#include "test/sensor_mock.hpp"
14#include "test/helpers.hpp"
15
16using ::testing::IsNull;
17using ::testing::Return;
18using ::testing::StrEq;
19using ::testing::_;
20
21static std::string modeInterface = "xyz.openbmc_project.Control.Mode";
22
23namespace {
24
25TEST(PidZoneConstructorTest, BoringConstructorTest) {
26 // Build a PID Zone.
27
28 sdbusplus::SdBusMock sdbus_mock_passive, sdbus_mock_host, sdbus_mock_mode;
29 auto bus_mock_passive = sdbusplus::get_mocked_new(&sdbus_mock_passive);
30 auto bus_mock_host = sdbusplus::get_mocked_new(&sdbus_mock_host);
31 auto bus_mock_mode = sdbusplus::get_mocked_new(&sdbus_mock_mode);
32
33 EXPECT_CALL(sdbus_mock_host,
34 sd_bus_add_object_manager(
35 IsNull(),
36 _,
37 StrEq("/xyz/openbmc_project/extsensors")))
38 .WillOnce(Return(0));
39
40 SensorManager m(std::move(bus_mock_passive),
41 std::move(bus_mock_host));
42
43 bool defer = true;
44 const char *objPath = "/path/";
45 int64_t zone = 1;
46 float minThermalRpm = 1000.0;
47 float failSafePercent = 0.75;
48
49 int i;
50 std::vector<std::string> properties;
51 SetupDbusObject(&sdbus_mock_mode, defer, objPath, modeInterface,
52 properties, &i);
53
54 PIDZone p(zone, minThermalRpm, failSafePercent, m, bus_mock_mode, objPath,
55 defer);
56 // Success.
57}
58
59}
60
61class PidZoneTest : public ::testing::Test {
62 protected:
63 PidZoneTest()
64 : property_index(),
65 properties(),
66 sdbus_mock_passive(),
67 sdbus_mock_host(),
68 sdbus_mock_mode()
69 {
70 EXPECT_CALL(sdbus_mock_host,
71 sd_bus_add_object_manager(
72 IsNull(),
73 _,
74 StrEq("/xyz/openbmc_project/extsensors")))
75 .WillOnce(Return(0));
76
77 auto bus_mock_passive =
78 sdbusplus::get_mocked_new(&sdbus_mock_passive);
79 auto bus_mock_host = sdbusplus::get_mocked_new(&sdbus_mock_host);
80 auto bus_mock_mode = sdbusplus::get_mocked_new(&sdbus_mock_mode);
81
82 // Compiler weirdly not happy about just instantiating mgr(...);
83 SensorManager m(std::move(bus_mock_passive),
84 std::move(bus_mock_host));
85 mgr = std::move(m);
86
87 SetupDbusObject(&sdbus_mock_mode, defer, objPath, modeInterface,
88 properties, &property_index);
89
90 zone = std::make_unique<PIDZone>(zoneId, minThermalRpm,
91 failSafePercent, mgr,
92 bus_mock_mode, objPath, defer);
93 }
94
95 // unused
96 int property_index;
97 std::vector<std::string> properties;
98
99 sdbusplus::SdBusMock sdbus_mock_passive;
100 sdbusplus::SdBusMock sdbus_mock_host;
101 sdbusplus::SdBusMock sdbus_mock_mode;
102 int64_t zoneId = 1;
103 float minThermalRpm = 1000.0;
104 float failSafePercent = 0.75;
105 bool defer = true;
106 const char *objPath = "/path/";
107 SensorManager mgr;
108
109 std::unique_ptr<PIDZone> zone;
110};
111
112TEST_F(PidZoneTest, GetZoneId_ReturnsExpected) {
113 // Verifies the zoneId returned is what we expect.
114
115 EXPECT_EQ(zoneId, zone->getZoneId());
116}
117
118TEST_F(PidZoneTest, GetAndSetManualModeTest_BehavesAsExpected) {
119 // Verifies that the zone starts in manual mode. Verifies that one can set
120 // the mode.
121 EXPECT_FALSE(zone->getManualMode());
122
123 zone->setManualMode(true);
124 EXPECT_TRUE(zone->getManualMode());
125}
126
127TEST_F(PidZoneTest, RpmSetPoints_AddMaxClear_BehaveAsExpected) {
128 // Tests addRPMSetPoint, clearRPMSetPoints, determineMaxRPMRequest
129 // and getMinThermalRpmSetPt.
130
131 // At least one value must be above the minimum thermal setpoint used in
132 // the constructor otherwise it'll choose that value
133 std::vector<float> values = {100, 200, 300, 400, 500, 5000};
134 for (auto v : values)
135 {
136 zone->addRPMSetPoint(v);
137 }
138
139 // This will pull the maximum RPM setpoint request.
140 zone->determineMaxRPMRequest();
141 EXPECT_EQ(5000, zone->getMaxRPMRequest());
142
143 // Clear the values, so it'll choose the minimum thermal setpoint.
144 zone->clearRPMSetPoints();
145
146 // This will go through the RPM set point values and grab the maximum.
147 zone->determineMaxRPMRequest();
148 EXPECT_EQ(zone->getMinThermalRpmSetPt(), zone->getMaxRPMRequest());
149}
150
151TEST_F(PidZoneTest, RpmSetPoints_AddBelowMinimum_BehavesAsExpected) {
152 // Tests adding several RPM setpoints, however, they're all lower than the
153 // configured minimal thermal set-point RPM value.
154
155 std::vector<float> values = {100, 200, 300, 400, 500};
156 for (auto v : values)
157 {
158 zone->addRPMSetPoint(v);
159 }
160
161 // This will pull the maximum RPM setpoint request.
162 zone->determineMaxRPMRequest();
163
164 // Verifies the value returned in the minimal thermal rpm set point.
165 EXPECT_EQ(zone->getMinThermalRpmSetPt(), zone->getMaxRPMRequest());
166}
167
168TEST_F(PidZoneTest, GetFailSafePercent_ReturnsExpected) {
169 // Verify the value used to create the object is stored.
170 EXPECT_EQ(failSafePercent, zone->getFailSafePercent());
171}
172
173TEST_F(PidZoneTest, ThermalInputs_FailsafeToValid_ReadsSensors) {
174 // This test will add a couple thermal inputs, and verify that the zone
175 // initializes into failsafe mode, and will read each sensor.
176
177 std::string name1 = "temp1";
178 int64_t timeout = 1;
179
180 std::unique_ptr<Sensor> sensor1 =
181 std::make_unique<SensorMock>(name1, timeout);
182 SensorMock *sensor_ptr1 = reinterpret_cast<SensorMock*>(sensor1.get());
183
184 std::string name2 = "temp2";
185 std::unique_ptr<Sensor> sensor2 =
186 std::make_unique<SensorMock>(name2, timeout);
187 SensorMock *sensor_ptr2 = reinterpret_cast<SensorMock*>(sensor2.get());
188
189 std::string type = "unchecked";
190 mgr.addSensor(type, name1, std::move(sensor1));
191 EXPECT_EQ(mgr.getSensor(name1), sensor_ptr1);
192 mgr.addSensor(type, name2, std::move(sensor2));
193 EXPECT_EQ(mgr.getSensor(name2), sensor_ptr2);
194
195 // Now that the sensors exist, add them to the zone.
196 zone->addThermalInput(name1);
197 zone->addThermalInput(name2);
198
199 // Initialize Zone
200 zone->initializeCache();
201
202 // Verify now in failsafe mode.
203 EXPECT_TRUE(zone->getFailSafeMode());
204
205 ReadReturn r1;
206 r1.value = 10.0;
207 r1.updated = std::chrono::high_resolution_clock::now();
208 EXPECT_CALL(*sensor_ptr1, read()).WillOnce(Return(r1));
209
210 ReadReturn r2;
211 r2.value = 11.0;
212 r2.updated = std::chrono::high_resolution_clock::now();
213 EXPECT_CALL(*sensor_ptr2, read()).WillOnce(Return(r2));
214
215 // Read the sensors, this will put the values into the cache.
216 zone->updateSensors();
217
218 // We should no longer be in failsafe mode.
219 EXPECT_FALSE(zone->getFailSafeMode());
220
221 EXPECT_EQ(r1.value, zone->getCachedValue(name1));
222 EXPECT_EQ(r2.value, zone->getCachedValue(name2));
223}
224
225TEST_F(PidZoneTest, FanInputTest_VerifiesFanValuesCached) {
226 // This will add a couple fan inputs, and verify the values are cached.
227
228 std::string name1 = "fan1";
229 int64_t timeout = 2;
230
231 std::unique_ptr<Sensor> sensor1 =
232 std::make_unique<SensorMock>(name1, timeout);
233 SensorMock *sensor_ptr1 = reinterpret_cast<SensorMock*>(sensor1.get());
234
235 std::string name2 = "fan2";
236 std::unique_ptr<Sensor> sensor2 =
237 std::make_unique<SensorMock>(name2, timeout);
238 SensorMock *sensor_ptr2 = reinterpret_cast<SensorMock*>(sensor2.get());
239
240 std::string type = "unchecked";
241 mgr.addSensor(type, name1, std::move(sensor1));
242 EXPECT_EQ(mgr.getSensor(name1), sensor_ptr1);
243 mgr.addSensor(type, name2, std::move(sensor2));
244 EXPECT_EQ(mgr.getSensor(name2), sensor_ptr2);
245
246 // Now that the sensors exist, add them to the zone.
247 zone->addFanInput(name1);
248 zone->addFanInput(name2);
249
250 // Initialize Zone
251 zone->initializeCache();
252
253 ReadReturn r1;
254 r1.value = 10.0;
255 r1.updated = std::chrono::high_resolution_clock::now();
256 EXPECT_CALL(*sensor_ptr1, read()).WillOnce(Return(r1));
257
258 ReadReturn r2;
259 r2.value = 11.0;
260 r2.updated = std::chrono::high_resolution_clock::now();
261 EXPECT_CALL(*sensor_ptr2, read()).WillOnce(Return(r2));
262
263 // Method under test will read through each fan sensor for the zone and
264 // cache the values.
265 zone->updateFanTelemetry();
266
267 EXPECT_EQ(r1.value, zone->getCachedValue(name1));
268 EXPECT_EQ(r2.value, zone->getCachedValue(name2));
269}
270
271TEST_F(PidZoneTest, ThermalInput_ValueTimeoutEntersFailSafeMode) {
272 // On the second updateSensors call, the updated timestamp will be beyond
273 // the timeout limit.
274
275 int64_t timeout = 1;
276
277 std::string name1 = "temp1";
278 std::unique_ptr<Sensor> sensor1 =
279 std::make_unique<SensorMock>(name1, timeout);
280 SensorMock *sensor_ptr1 = reinterpret_cast<SensorMock*>(sensor1.get());
281
282 std::string name2 = "temp2";
283 std::unique_ptr<Sensor> sensor2 =
284 std::make_unique<SensorMock>(name2, timeout);
285 SensorMock *sensor_ptr2 = reinterpret_cast<SensorMock*>(sensor2.get());
286
287 std::string type = "unchecked";
288 mgr.addSensor(type, name1, std::move(sensor1));
289 EXPECT_EQ(mgr.getSensor(name1), sensor_ptr1);
290 mgr.addSensor(type, name2, std::move(sensor2));
291 EXPECT_EQ(mgr.getSensor(name2), sensor_ptr2);
292
293 zone->addThermalInput(name1);
294 zone->addThermalInput(name2);
295
296 // Initialize Zone
297 zone->initializeCache();
298
299 // Verify now in failsafe mode.
300 EXPECT_TRUE(zone->getFailSafeMode());
301
302 ReadReturn r1;
303 r1.value = 10.0;
304 r1.updated = std::chrono::high_resolution_clock::now();
305 EXPECT_CALL(*sensor_ptr1, read()).WillOnce(Return(r1));
306
307 ReadReturn r2;
308 r2.value = 11.0;
309 r2.updated = std::chrono::high_resolution_clock::now();
310 EXPECT_CALL(*sensor_ptr2, read()).WillOnce(Return(r2));
311
312 zone->updateSensors();
313 EXPECT_FALSE(zone->getFailSafeMode());
314
315 // Ok, so we're not in failsafe mode, so let's set updated to the past.
316 // sensor1 will have an updated field older than its timeout value, but
317 // sensor2 will be fine. :D
318 r1.updated -= std::chrono::seconds(3);
319 r2.updated = std::chrono::high_resolution_clock::now();
320
321 EXPECT_CALL(*sensor_ptr1, read()).WillOnce(Return(r1));
322 EXPECT_CALL(*sensor_ptr2, read()).WillOnce(Return(r2));
323
324 // Method under test will read each sensor. One sensor's value is older
325 // than the timeout for that sensor and this triggers failsafe mode.
326 zone->updateSensors();
327 EXPECT_TRUE(zone->getFailSafeMode());
328}
329
330TEST_F(PidZoneTest, GetSensorTest_ReturnsExpected) {
331 // One can grab a sensor from the manager through the zone.
332
333 int64_t timeout = 1;
334
335 std::string name1 = "temp1";
336 std::unique_ptr<Sensor> sensor1 =
337 std::make_unique<SensorMock>(name1, timeout);
338 SensorMock *sensor_ptr1 = reinterpret_cast<SensorMock*>(sensor1.get());
339
340 std::string type = "unchecked";
341 mgr.addSensor(type, name1, std::move(sensor1));
342 EXPECT_EQ(mgr.getSensor(name1), sensor_ptr1);
343
344 zone->addThermalInput(name1);
345
346 // Verify method under test returns the pointer we expect.
347 EXPECT_EQ(mgr.getSensor(name1), zone->getSensor(name1));
348}
349
350TEST_F(PidZoneTest, AddThermalPIDTest_VerifiesThermalPIDsProcessed) {
351 // Tests adding a thermal PID controller to the zone, and verifies it's
352 // touched during processing.
353
354 std::unique_ptr<PIDController> tpid =
355 std::make_unique<ControllerMock>("thermal1", zone.get());
356 ControllerMock *tmock = reinterpret_cast<ControllerMock*>(tpid.get());
357
358 // Access the internal pid configuration to clear it out (unrelated to the
359 // test).
360 ec::pid_info_t* info = tpid->get_pid_info();
361 std::memset(info, 0x00, sizeof(ec::pid_info_t));
362
363 zone->addThermalPID(std::move(tpid));
364
365 EXPECT_CALL(*tmock, setpt_proc()).WillOnce(Return(10.0));
366 EXPECT_CALL(*tmock, input_proc()).WillOnce(Return(11.0));
367 EXPECT_CALL(*tmock, output_proc(_));
368
369 // Method under test will, for each thermal PID, call setpt, input, and
370 // output.
371 zone->process_thermals();
372}
373
374TEST_F(PidZoneTest, AddFanPIDTest_VerifiesFanPIDsProcessed) {
375 // Tests adding a fan PID controller to the zone, and verifies it's
376 // touched during processing.
377
378 std::unique_ptr<PIDController> tpid =
379 std::make_unique<ControllerMock>("fan1", zone.get());
380 ControllerMock *tmock = reinterpret_cast<ControllerMock*>(tpid.get());
381
382 // Access the internal pid configuration to clear it out (unrelated to the
383 // test).
384 ec::pid_info_t* info = tpid->get_pid_info();
385 std::memset(info, 0x00, sizeof(ec::pid_info_t));
386
387 zone->addFanPID(std::move(tpid));
388
389 EXPECT_CALL(*tmock, setpt_proc()).WillOnce(Return(10.0));
390 EXPECT_CALL(*tmock, input_proc()).WillOnce(Return(11.0));
391 EXPECT_CALL(*tmock, output_proc(_));
392
393 // Method under test will, for each fan PID, call setpt, input, and output.
394 zone->process_fans();
395}
396
397TEST_F(PidZoneTest, ManualModeDbusTest_VerifySetManualBehavesAsExpected) {
398 // The manual(bool) method is inherited from the dbus mode interface.
399
400 // Verifies that someone doesn't remove the internal call to the dbus
401 // object from which we're inheriting.
402 EXPECT_CALL(sdbus_mock_mode,
403 sd_bus_emit_properties_changed_strv(IsNull(), StrEq(objPath),
404 StrEq(modeInterface),
405 NotNull()))
406 .WillOnce(Invoke([&](sd_bus *bus, const char *path,
407 const char *interface, char **names) {
408 EXPECT_STREQ("Manual", names[0]);
409 return 0;
410 }));
411
412 // Method under test will set the manual mode to true and broadcast this
413 // change on dbus.
414 zone->manual(true);
415 EXPECT_TRUE(zone->getManualMode());
416}
417
418TEST_F(PidZoneTest, FailsafeDbusTest_VerifiesReturnsExpected) {
419 // This property is implemented by us as read-only, such that trying to
420 // write to it will have no effect.
421 EXPECT_EQ(zone->failSafe(), zone->getFailSafeMode());
422}