blob: b1279291c8b83c55868d599fbfd1abef44a24107 [file] [log] [blame]
Patrick Venture566a1512018-06-12 14:51:07 -07001#include "pid/fancontroller.hpp"
2
3#include <gmock/gmock.h>
4#include <gtest/gtest.h>
5#include <string>
6#include <vector>
7
8#include "pid/ec/pid.hpp"
9#include "test/sensor_mock.hpp"
10#include "test/zone_mock.hpp"
11
12using ::testing::DoubleEq;
13using ::testing::Invoke;
14using ::testing::Return;
15using ::testing::StrEq;
16using ::testing::_;
17
18TEST(FanControllerTest, BoringFactoryTest) {
19 // Verify the factory will properly build the FanPIDController in the
20 // boring (uninteresting) case.
21 ZoneMock z;
22
23 std::vector<std::string> inputs = {"fan0"};
24 ec::pidinfo initial;
25
26 std::unique_ptr<PIDController> p =
27 FanController::CreateFanPid(&z, "fan1", inputs, initial);
28 // Success
29 EXPECT_FALSE(p == nullptr);
30}
31
32TEST(FanControllerTest, VerifyFactoryFailsWithZeroInputs) {
33 // A fan controller needs at least one input.
34
35 ZoneMock z;
36
37 std::vector<std::string> inputs = {};
38 ec::pidinfo initial;
39
40 std::unique_ptr<PIDController> p =
41 FanController::CreateFanPid(&z, "fan1", inputs, initial);
42 EXPECT_TRUE(p == nullptr);
43}
44
45TEST(FanControllerTest, InputProc_AllSensorsReturnZero) {
46 // If all your inputs are 0, return 0.
47
48 ZoneMock z;
49
50 std::vector<std::string> inputs = {"fan0", "fan1"};
51 ec::pidinfo initial;
52
53 std::unique_ptr<PIDController> p =
54 FanController::CreateFanPid(&z, "fan1", inputs, initial);
55 EXPECT_FALSE(p == nullptr);
56
57 EXPECT_CALL(z, getCachedValue(StrEq("fan0"))).WillOnce(Return(0));
58 EXPECT_CALL(z, getCachedValue(StrEq("fan1"))).WillOnce(Return(0));
59
60 EXPECT_EQ(0.0, p->input_proc());
61}
62
63TEST(FanControllerTest, InputProc_IfSensorNegativeIsIgnored) {
64 // A sensor value returning sub-zero is ignored as an error.
65 ZoneMock z;
66
67 std::vector<std::string> inputs = {"fan0", "fan1"};
68 ec::pidinfo initial;
69
70 std::unique_ptr<PIDController> p =
71 FanController::CreateFanPid(&z, "fan1", inputs, initial);
72 EXPECT_FALSE(p == nullptr);
73
74 EXPECT_CALL(z, getCachedValue(StrEq("fan0"))).WillOnce(Return(-1));
75 EXPECT_CALL(z, getCachedValue(StrEq("fan1"))).WillOnce(Return(-1));
76
77 EXPECT_EQ(0.0, p->input_proc());
78}
79
80TEST(FanControllerTest, InputProc_ChoosesMinimumValue) {
81 // Verify it selects the minimum value from its inputs.
82
83 ZoneMock z;
84
85 std::vector<std::string> inputs = {"fan0", "fan1", "fan2"};
86 ec::pidinfo initial;
87
88 std::unique_ptr<PIDController> p =
89 FanController::CreateFanPid(&z, "fan1", inputs, initial);
90 EXPECT_FALSE(p == nullptr);
91
92 EXPECT_CALL(z, getCachedValue(StrEq("fan0"))).WillOnce(Return(10.0));
93 EXPECT_CALL(z, getCachedValue(StrEq("fan1"))).WillOnce(Return(30.0));
94 EXPECT_CALL(z, getCachedValue(StrEq("fan2"))).WillOnce(Return(5.0));
95
96 EXPECT_EQ(5.0, p->input_proc());
97}
98
99// The direction is unused presently, but these tests validate the logic.
100TEST(FanControllerTest, SetPtProc_SpeedChanges_VerifyDirection) {
101 // The fan direction defaults to neutral, because we have no data. Verify
102 // that after this point it appropriately will indicate speeding up or
103 // slowing down based on the RPM values specified.
104
105 ZoneMock z;
106
107 std::vector<std::string> inputs = {"fan0", "fan1"};
108 ec::pidinfo initial;
109
110 std::unique_ptr<PIDController> p =
111 FanController::CreateFanPid(&z, "fan1", inputs, initial);
112 EXPECT_FALSE(p == nullptr);
113 // Grab pointer for mocking.
114 FanController *fp = reinterpret_cast<FanController*>(p.get());
115
116 // Fanspeed starts are Neutral.
117 EXPECT_EQ(FanSpeedDirection::NEUTRAL, fp->getFanDirection());
118
119 // getMaxRPMRequest returns a higher value than 0, so the fans should be
120 // marked as speeding up.
121 EXPECT_CALL(z, getMaxRPMRequest()).WillOnce(Return(10.0));
122 EXPECT_EQ(10.0, p->setpt_proc());
123 EXPECT_EQ(FanSpeedDirection::UP, fp->getFanDirection());
124
125 // getMaxRPMRequest returns a lower value than 10, so the fans should be
126 // marked as slowing down.
127 EXPECT_CALL(z, getMaxRPMRequest()).WillOnce(Return(5.0));
128 EXPECT_EQ(5.0, p->setpt_proc());
129 EXPECT_EQ(FanSpeedDirection::DOWN, fp->getFanDirection());
130
131 // getMaxRPMRequest returns the same value, so the fans should be marked as
132 // neutral.
133 EXPECT_CALL(z, getMaxRPMRequest()).WillOnce(Return(5.0));
134 EXPECT_EQ(5.0, p->setpt_proc());
135 EXPECT_EQ(FanSpeedDirection::NEUTRAL, fp->getFanDirection());
136}
137
138TEST(FanControllerTest, OutputProc_VerifiesIfFailsafeEnabledInputIsIgnored) {
139 // Verify that if failsafe mode is enabled and the input value for the fans
140 // is below the failsafe minimum value, the input is not used and the fans
141 // are driven at failsafe RPM.
142
143 ZoneMock z;
144
145 std::vector<std::string> inputs = {"fan0", "fan1"};
146 ec::pidinfo initial;
147
148 std::unique_ptr<PIDController> p =
149 FanController::CreateFanPid(&z, "fan1", inputs, initial);
150 EXPECT_FALSE(p == nullptr);
151
152 EXPECT_CALL(z, getFailSafeMode()).WillOnce(Return(true));
153 EXPECT_CALL(z, getFailSafePercent()).Times(2).WillRepeatedly(Return(75.0));
154
155 int64_t timeout = 0;
156 std::unique_ptr<Sensor> s1 = std::make_unique<SensorMock>("fan0", timeout);
157 std::unique_ptr<Sensor> s2 = std::make_unique<SensorMock>("fan1", timeout);
158 // Grab pointers for mocking.
159 SensorMock *sm1 = reinterpret_cast<SensorMock*>(s1.get());
160 SensorMock *sm2 = reinterpret_cast<SensorMock*>(s2.get());
161
162 EXPECT_CALL(z, getSensor(StrEq("fan0"))).WillOnce(Return(s1.get()));
163 EXPECT_CALL(*sm1, write(0.75));
164 EXPECT_CALL(z, getSensor(StrEq("fan1"))).WillOnce(Return(s2.get()));
165 EXPECT_CALL(*sm2, write(0.75));
166
167 // This is a fan PID, so calling output_proc will try to write this value
168 // to the sensors.
169
170 // Setting 50%, will end up being 75% because the sensors are in failsafe mode.
171 p->output_proc(50.0);
172}
173
174TEST(FanControllerTest, OutputProc_BehavesAsExpected) {
175 // Verifies that when the system is not in failsafe mode, the input value
176 // to output_proc is used to drive the sensors (fans).
177
178 ZoneMock z;
179
180 std::vector<std::string> inputs = {"fan0", "fan1"};
181 ec::pidinfo initial;
182
183 std::unique_ptr<PIDController> p =
184 FanController::CreateFanPid(&z, "fan1", inputs, initial);
185 EXPECT_FALSE(p == nullptr);
186
187 EXPECT_CALL(z, getFailSafeMode()).WillOnce(Return(false));
188
189 int64_t timeout = 0;
190 std::unique_ptr<Sensor> s1 = std::make_unique<SensorMock>("fan0", timeout);
191 std::unique_ptr<Sensor> s2 = std::make_unique<SensorMock>("fan1", timeout);
192 // Grab pointers for mocking.
193 SensorMock *sm1 = reinterpret_cast<SensorMock*>(s1.get());
194 SensorMock *sm2 = reinterpret_cast<SensorMock*>(s2.get());
195
196 EXPECT_CALL(z, getSensor(StrEq("fan0"))).WillOnce(Return(s1.get()));
197 EXPECT_CALL(*sm1, write(0.5));
198 EXPECT_CALL(z, getSensor(StrEq("fan1"))).WillOnce(Return(s2.get()));
199 EXPECT_CALL(*sm2, write(0.5));
200
201 // This is a fan PID, so calling output_proc will try to write this value
202 // to the sensors.
203 p->output_proc(50.0);
204}
205
206TEST(FanControllerTest, OutputProc_VerifyFailSafeIgnoredIfInputHigher) {
207 // If the requested output is higher than the failsafe value, then use the
208 // value provided to output_proc.
209
210 ZoneMock z;
211
212 std::vector<std::string> inputs = {"fan0"};
213 ec::pidinfo initial;
214
215 std::unique_ptr<PIDController> p =
216 FanController::CreateFanPid(&z, "fan1", inputs, initial);
217 EXPECT_FALSE(p == nullptr);
218
219 EXPECT_CALL(z, getFailSafeMode()).WillOnce(Return(true));
220 EXPECT_CALL(z, getFailSafePercent()).WillOnce(Return(75.0));
221
222 int64_t timeout = 0;
223 std::unique_ptr<Sensor> s1 = std::make_unique<SensorMock>("fan0", timeout);
224 // Grab pointer for mocking.
225 SensorMock *sm1 = reinterpret_cast<SensorMock*>(s1.get());
226
227 // Converting from float to double for expectation.
228 float percent = 80;
229 double value = static_cast<double>(percent / 100);
230
231 EXPECT_CALL(z, getSensor(StrEq("fan0"))).WillOnce(Return(s1.get()));
232 EXPECT_CALL(*sm1, write(value));
233
234 // This is a fan PID, so calling output_proc will try to write this value
235 // to the sensors.
236 p->output_proc(percent);
237}