blob: 44df7b1e6005cfea33a1645e4a86c489eb6fae04 [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
8#include <chrono>
9#include <cstring>
Patrick Venturea58197c2018-06-11 15:29:45 -070010#include <sdbusplus/test/sdbus_mock.hpp>
11#include <vector>
12
Patrick Ventureda4a5dd2018-08-31 09:42:48 -070013#include <gmock/gmock.h>
14#include <gtest/gtest.h>
Patrick Venturea58197c2018-06-11 15:29:45 -070015
Patrick Ventureda4a5dd2018-08-31 09:42:48 -070016using ::testing::_;
Patrick Venturea58197c2018-06-11 15:29:45 -070017using ::testing::IsNull;
18using ::testing::Return;
19using ::testing::StrEq;
Patrick Venturea58197c2018-06-11 15:29:45 -070020
21static std::string modeInterface = "xyz.openbmc_project.Control.Mode";
22
Patrick Ventureda4a5dd2018-08-31 09:42:48 -070023namespace
24{
Patrick Venturea58197c2018-06-11 15:29:45 -070025
Patrick Ventureda4a5dd2018-08-31 09:42:48 -070026TEST(PidZoneConstructorTest, BoringConstructorTest)
27{
Patrick Venturea58197c2018-06-11 15:29:45 -070028 // Build a PID Zone.
29
30 sdbusplus::SdBusMock sdbus_mock_passive, sdbus_mock_host, sdbus_mock_mode;
31 auto bus_mock_passive = sdbusplus::get_mocked_new(&sdbus_mock_passive);
32 auto bus_mock_host = sdbusplus::get_mocked_new(&sdbus_mock_host);
33 auto bus_mock_mode = sdbusplus::get_mocked_new(&sdbus_mock_mode);
34
35 EXPECT_CALL(sdbus_mock_host,
36 sd_bus_add_object_manager(
Patrick Ventureda4a5dd2018-08-31 09:42:48 -070037 IsNull(), _, StrEq("/xyz/openbmc_project/extsensors")))
Patrick Venturea58197c2018-06-11 15:29:45 -070038 .WillOnce(Return(0));
39
Patrick Ventureda4a5dd2018-08-31 09:42:48 -070040 SensorManager m(std::move(bus_mock_passive), std::move(bus_mock_host));
Patrick Venturea58197c2018-06-11 15:29:45 -070041
42 bool defer = true;
Patrick Venturee2ec0f62018-09-04 12:30:27 -070043 const char* objPath = "/path/";
Patrick Venturea58197c2018-06-11 15:29:45 -070044 int64_t zone = 1;
45 float minThermalRpm = 1000.0;
46 float failSafePercent = 0.75;
47
48 int i;
49 std::vector<std::string> properties;
Patrick Ventureda4a5dd2018-08-31 09:42:48 -070050 SetupDbusObject(&sdbus_mock_mode, defer, objPath, modeInterface, properties,
51 &i);
Patrick Venturea58197c2018-06-11 15:29:45 -070052
53 PIDZone p(zone, minThermalRpm, failSafePercent, m, bus_mock_mode, objPath,
54 defer);
55 // Success.
56}
57
Patrick Ventureda4a5dd2018-08-31 09:42:48 -070058} // namespace
Patrick Venturea58197c2018-06-11 15:29:45 -070059
Patrick Ventureda4a5dd2018-08-31 09:42:48 -070060class PidZoneTest : public ::testing::Test
61{
62 protected:
63 PidZoneTest() :
64 property_index(), properties(), sdbus_mock_passive(), sdbus_mock_host(),
65 sdbus_mock_mode()
66 {
67 EXPECT_CALL(sdbus_mock_host,
68 sd_bus_add_object_manager(
69 IsNull(), _, StrEq("/xyz/openbmc_project/extsensors")))
70 .WillOnce(Return(0));
Patrick Venturea58197c2018-06-11 15:29:45 -070071
Patrick Ventureda4a5dd2018-08-31 09:42:48 -070072 auto bus_mock_passive = sdbusplus::get_mocked_new(&sdbus_mock_passive);
73 auto bus_mock_host = sdbusplus::get_mocked_new(&sdbus_mock_host);
74 auto bus_mock_mode = sdbusplus::get_mocked_new(&sdbus_mock_mode);
Patrick Venturea58197c2018-06-11 15:29:45 -070075
Patrick Ventureda4a5dd2018-08-31 09:42:48 -070076 // Compiler weirdly not happy about just instantiating mgr(...);
77 SensorManager m(std::move(bus_mock_passive), std::move(bus_mock_host));
78 mgr = std::move(m);
Patrick Venturea58197c2018-06-11 15:29:45 -070079
Patrick Ventureda4a5dd2018-08-31 09:42:48 -070080 SetupDbusObject(&sdbus_mock_mode, defer, objPath, modeInterface,
81 properties, &property_index);
Patrick Venturea58197c2018-06-11 15:29:45 -070082
Patrick Ventureda4a5dd2018-08-31 09:42:48 -070083 zone = std::make_unique<PIDZone>(zoneId, minThermalRpm, failSafePercent,
84 mgr, bus_mock_mode, objPath, defer);
85 }
Patrick Venturea58197c2018-06-11 15:29:45 -070086
Patrick Ventureda4a5dd2018-08-31 09:42:48 -070087 // unused
88 int property_index;
89 std::vector<std::string> properties;
Patrick Venturea58197c2018-06-11 15:29:45 -070090
Patrick Ventureda4a5dd2018-08-31 09:42:48 -070091 sdbusplus::SdBusMock sdbus_mock_passive;
92 sdbusplus::SdBusMock sdbus_mock_host;
93 sdbusplus::SdBusMock sdbus_mock_mode;
94 int64_t zoneId = 1;
95 float minThermalRpm = 1000.0;
96 float failSafePercent = 0.75;
97 bool defer = true;
Patrick Venturee2ec0f62018-09-04 12:30:27 -070098 const char* objPath = "/path/";
Patrick Ventureda4a5dd2018-08-31 09:42:48 -070099 SensorManager mgr;
Patrick Venturea58197c2018-06-11 15:29:45 -0700100
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700101 std::unique_ptr<PIDZone> zone;
Patrick Venturea58197c2018-06-11 15:29:45 -0700102};
103
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700104TEST_F(PidZoneTest, GetZoneId_ReturnsExpected)
105{
Patrick Venturea58197c2018-06-11 15:29:45 -0700106 // Verifies the zoneId returned is what we expect.
107
108 EXPECT_EQ(zoneId, zone->getZoneId());
109}
110
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700111TEST_F(PidZoneTest, GetAndSetManualModeTest_BehavesAsExpected)
112{
Patrick Venturea58197c2018-06-11 15:29:45 -0700113 // Verifies that the zone starts in manual mode. Verifies that one can set
114 // the mode.
115 EXPECT_FALSE(zone->getManualMode());
116
117 zone->setManualMode(true);
118 EXPECT_TRUE(zone->getManualMode());
119}
120
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700121TEST_F(PidZoneTest, RpmSetPoints_AddMaxClear_BehaveAsExpected)
122{
Patrick Venturea58197c2018-06-11 15:29:45 -0700123 // Tests addRPMSetPoint, clearRPMSetPoints, determineMaxRPMRequest
124 // and getMinThermalRpmSetPt.
125
126 // At least one value must be above the minimum thermal setpoint used in
127 // the constructor otherwise it'll choose that value
128 std::vector<float> values = {100, 200, 300, 400, 500, 5000};
129 for (auto v : values)
130 {
131 zone->addRPMSetPoint(v);
132 }
133
134 // This will pull the maximum RPM setpoint request.
135 zone->determineMaxRPMRequest();
136 EXPECT_EQ(5000, zone->getMaxRPMRequest());
137
138 // Clear the values, so it'll choose the minimum thermal setpoint.
139 zone->clearRPMSetPoints();
140
141 // This will go through the RPM set point values and grab the maximum.
142 zone->determineMaxRPMRequest();
143 EXPECT_EQ(zone->getMinThermalRpmSetPt(), zone->getMaxRPMRequest());
144}
145
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700146TEST_F(PidZoneTest, RpmSetPoints_AddBelowMinimum_BehavesAsExpected)
147{
Patrick Venturea58197c2018-06-11 15:29:45 -0700148 // Tests adding several RPM setpoints, however, they're all lower than the
149 // configured minimal thermal set-point RPM value.
150
151 std::vector<float> values = {100, 200, 300, 400, 500};
152 for (auto v : values)
153 {
154 zone->addRPMSetPoint(v);
155 }
156
157 // This will pull the maximum RPM setpoint request.
158 zone->determineMaxRPMRequest();
159
160 // Verifies the value returned in the minimal thermal rpm set point.
161 EXPECT_EQ(zone->getMinThermalRpmSetPt(), zone->getMaxRPMRequest());
162}
163
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700164TEST_F(PidZoneTest, GetFailSafePercent_ReturnsExpected)
165{
Patrick Venturea58197c2018-06-11 15:29:45 -0700166 // Verify the value used to create the object is stored.
167 EXPECT_EQ(failSafePercent, zone->getFailSafePercent());
168}
169
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700170TEST_F(PidZoneTest, ThermalInputs_FailsafeToValid_ReadsSensors)
171{
Patrick Venturea58197c2018-06-11 15:29:45 -0700172 // This test will add a couple thermal inputs, and verify that the zone
173 // initializes into failsafe mode, and will read each sensor.
174
175 std::string name1 = "temp1";
176 int64_t timeout = 1;
177
178 std::unique_ptr<Sensor> sensor1 =
179 std::make_unique<SensorMock>(name1, timeout);
Patrick Venturee2ec0f62018-09-04 12:30:27 -0700180 SensorMock* sensor_ptr1 = reinterpret_cast<SensorMock*>(sensor1.get());
Patrick Venturea58197c2018-06-11 15:29:45 -0700181
182 std::string name2 = "temp2";
183 std::unique_ptr<Sensor> sensor2 =
184 std::make_unique<SensorMock>(name2, timeout);
Patrick Venturee2ec0f62018-09-04 12:30:27 -0700185 SensorMock* sensor_ptr2 = reinterpret_cast<SensorMock*>(sensor2.get());
Patrick Venturea58197c2018-06-11 15:29:45 -0700186
187 std::string type = "unchecked";
188 mgr.addSensor(type, name1, std::move(sensor1));
189 EXPECT_EQ(mgr.getSensor(name1), sensor_ptr1);
190 mgr.addSensor(type, name2, std::move(sensor2));
191 EXPECT_EQ(mgr.getSensor(name2), sensor_ptr2);
192
193 // Now that the sensors exist, add them to the zone.
194 zone->addThermalInput(name1);
195 zone->addThermalInput(name2);
196
197 // Initialize Zone
198 zone->initializeCache();
199
200 // Verify now in failsafe mode.
201 EXPECT_TRUE(zone->getFailSafeMode());
202
203 ReadReturn r1;
204 r1.value = 10.0;
205 r1.updated = std::chrono::high_resolution_clock::now();
206 EXPECT_CALL(*sensor_ptr1, read()).WillOnce(Return(r1));
207
208 ReadReturn r2;
209 r2.value = 11.0;
210 r2.updated = std::chrono::high_resolution_clock::now();
211 EXPECT_CALL(*sensor_ptr2, read()).WillOnce(Return(r2));
212
213 // Read the sensors, this will put the values into the cache.
214 zone->updateSensors();
215
216 // We should no longer be in failsafe mode.
217 EXPECT_FALSE(zone->getFailSafeMode());
218
219 EXPECT_EQ(r1.value, zone->getCachedValue(name1));
220 EXPECT_EQ(r2.value, zone->getCachedValue(name2));
221}
222
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700223TEST_F(PidZoneTest, FanInputTest_VerifiesFanValuesCached)
224{
Patrick Venturea58197c2018-06-11 15:29:45 -0700225 // This will add a couple fan inputs, and verify the values are cached.
226
227 std::string name1 = "fan1";
228 int64_t timeout = 2;
229
230 std::unique_ptr<Sensor> sensor1 =
231 std::make_unique<SensorMock>(name1, timeout);
Patrick Venturee2ec0f62018-09-04 12:30:27 -0700232 SensorMock* sensor_ptr1 = reinterpret_cast<SensorMock*>(sensor1.get());
Patrick Venturea58197c2018-06-11 15:29:45 -0700233
234 std::string name2 = "fan2";
235 std::unique_ptr<Sensor> sensor2 =
236 std::make_unique<SensorMock>(name2, timeout);
Patrick Venturee2ec0f62018-09-04 12:30:27 -0700237 SensorMock* sensor_ptr2 = reinterpret_cast<SensorMock*>(sensor2.get());
Patrick Venturea58197c2018-06-11 15:29:45 -0700238
239 std::string type = "unchecked";
240 mgr.addSensor(type, name1, std::move(sensor1));
241 EXPECT_EQ(mgr.getSensor(name1), sensor_ptr1);
242 mgr.addSensor(type, name2, std::move(sensor2));
243 EXPECT_EQ(mgr.getSensor(name2), sensor_ptr2);
244
245 // Now that the sensors exist, add them to the zone.
246 zone->addFanInput(name1);
247 zone->addFanInput(name2);
248
249 // Initialize Zone
250 zone->initializeCache();
251
252 ReadReturn r1;
253 r1.value = 10.0;
254 r1.updated = std::chrono::high_resolution_clock::now();
255 EXPECT_CALL(*sensor_ptr1, read()).WillOnce(Return(r1));
256
257 ReadReturn r2;
258 r2.value = 11.0;
259 r2.updated = std::chrono::high_resolution_clock::now();
260 EXPECT_CALL(*sensor_ptr2, read()).WillOnce(Return(r2));
261
262 // Method under test will read through each fan sensor for the zone and
263 // cache the values.
264 zone->updateFanTelemetry();
265
266 EXPECT_EQ(r1.value, zone->getCachedValue(name1));
267 EXPECT_EQ(r2.value, zone->getCachedValue(name2));
268}
269
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700270TEST_F(PidZoneTest, ThermalInput_ValueTimeoutEntersFailSafeMode)
271{
Patrick Venturea58197c2018-06-11 15:29:45 -0700272 // 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);
Patrick Venturee2ec0f62018-09-04 12:30:27 -0700280 SensorMock* sensor_ptr1 = reinterpret_cast<SensorMock*>(sensor1.get());
Patrick Venturea58197c2018-06-11 15:29:45 -0700281
282 std::string name2 = "temp2";
283 std::unique_ptr<Sensor> sensor2 =
284 std::make_unique<SensorMock>(name2, timeout);
Patrick Venturee2ec0f62018-09-04 12:30:27 -0700285 SensorMock* sensor_ptr2 = reinterpret_cast<SensorMock*>(sensor2.get());
Patrick Venturea58197c2018-06-11 15:29:45 -0700286
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
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700330TEST_F(PidZoneTest, GetSensorTest_ReturnsExpected)
331{
Patrick Venturea58197c2018-06-11 15:29:45 -0700332 // One can grab a sensor from the manager through the zone.
333
334 int64_t timeout = 1;
335
336 std::string name1 = "temp1";
337 std::unique_ptr<Sensor> sensor1 =
338 std::make_unique<SensorMock>(name1, timeout);
Patrick Venturee2ec0f62018-09-04 12:30:27 -0700339 SensorMock* sensor_ptr1 = reinterpret_cast<SensorMock*>(sensor1.get());
Patrick Venturea58197c2018-06-11 15:29:45 -0700340
341 std::string type = "unchecked";
342 mgr.addSensor(type, name1, std::move(sensor1));
343 EXPECT_EQ(mgr.getSensor(name1), sensor_ptr1);
344
345 zone->addThermalInput(name1);
346
347 // Verify method under test returns the pointer we expect.
348 EXPECT_EQ(mgr.getSensor(name1), zone->getSensor(name1));
349}
350
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700351TEST_F(PidZoneTest, AddThermalPIDTest_VerifiesThermalPIDsProcessed)
352{
Patrick Venturea58197c2018-06-11 15:29:45 -0700353 // Tests adding a thermal PID controller to the zone, and verifies it's
354 // touched during processing.
355
356 std::unique_ptr<PIDController> tpid =
357 std::make_unique<ControllerMock>("thermal1", zone.get());
Patrick Venturee2ec0f62018-09-04 12:30:27 -0700358 ControllerMock* tmock = reinterpret_cast<ControllerMock*>(tpid.get());
Patrick Venturea58197c2018-06-11 15:29:45 -0700359
360 // Access the internal pid configuration to clear it out (unrelated to the
361 // test).
Patrick Venturee2ec0f62018-09-04 12:30:27 -0700362 ec::pid_info_t* info = tpid->get_pid_info();
Patrick Venturea58197c2018-06-11 15:29:45 -0700363 std::memset(info, 0x00, sizeof(ec::pid_info_t));
364
365 zone->addThermalPID(std::move(tpid));
366
367 EXPECT_CALL(*tmock, setpt_proc()).WillOnce(Return(10.0));
368 EXPECT_CALL(*tmock, input_proc()).WillOnce(Return(11.0));
369 EXPECT_CALL(*tmock, output_proc(_));
370
371 // Method under test will, for each thermal PID, call setpt, input, and
372 // output.
373 zone->process_thermals();
374}
375
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700376TEST_F(PidZoneTest, AddFanPIDTest_VerifiesFanPIDsProcessed)
377{
Patrick Venturea58197c2018-06-11 15:29:45 -0700378 // Tests adding a fan PID controller to the zone, and verifies it's
379 // touched during processing.
380
381 std::unique_ptr<PIDController> tpid =
382 std::make_unique<ControllerMock>("fan1", zone.get());
Patrick Venturee2ec0f62018-09-04 12:30:27 -0700383 ControllerMock* tmock = reinterpret_cast<ControllerMock*>(tpid.get());
Patrick Venturea58197c2018-06-11 15:29:45 -0700384
385 // Access the internal pid configuration to clear it out (unrelated to the
386 // test).
Patrick Venturee2ec0f62018-09-04 12:30:27 -0700387 ec::pid_info_t* info = tpid->get_pid_info();
Patrick Venturea58197c2018-06-11 15:29:45 -0700388 std::memset(info, 0x00, sizeof(ec::pid_info_t));
389
390 zone->addFanPID(std::move(tpid));
391
392 EXPECT_CALL(*tmock, setpt_proc()).WillOnce(Return(10.0));
393 EXPECT_CALL(*tmock, input_proc()).WillOnce(Return(11.0));
394 EXPECT_CALL(*tmock, output_proc(_));
395
396 // Method under test will, for each fan PID, call setpt, input, and output.
397 zone->process_fans();
398}
399
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700400TEST_F(PidZoneTest, ManualModeDbusTest_VerifySetManualBehavesAsExpected)
401{
Patrick Venturea58197c2018-06-11 15:29:45 -0700402 // The manual(bool) method is inherited from the dbus mode interface.
403
404 // Verifies that someone doesn't remove the internal call to the dbus
405 // object from which we're inheriting.
406 EXPECT_CALL(sdbus_mock_mode,
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700407 sd_bus_emit_properties_changed_strv(
408 IsNull(), StrEq(objPath), StrEq(modeInterface), NotNull()))
Patrick Venturee2ec0f62018-09-04 12:30:27 -0700409 .WillOnce(Invoke([&](sd_bus* bus, const char* path,
410 const char* interface, char** names) {
Patrick Venturea58197c2018-06-11 15:29:45 -0700411 EXPECT_STREQ("Manual", names[0]);
412 return 0;
413 }));
414
415 // Method under test will set the manual mode to true and broadcast this
416 // change on dbus.
417 zone->manual(true);
418 EXPECT_TRUE(zone->getManualMode());
419}
420
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700421TEST_F(PidZoneTest, FailsafeDbusTest_VerifiesReturnsExpected)
422{
Patrick Venturea58197c2018-06-11 15:29:45 -0700423 // This property is implemented by us as read-only, such that trying to
424 // write to it will have no effect.
425 EXPECT_EQ(zone->failSafe(), zone->getFailSafeMode());
426}