1 #include "pid/ec/logging.hpp"
2 #include "pid/ec/pid.hpp"
3 #include "pid/thermalcontroller.hpp"
4 #include "test/zone_mock.hpp"
5 
6 #include <string>
7 #include <vector>
8 
9 #include <gmock/gmock.h>
10 #include <gtest/gtest.h>
11 
12 namespace pid_control
13 {
14 namespace
15 {
16 
17 using ::testing::_;
18 using ::testing::Return;
19 using ::testing::StrEq;
20 
21 TEST(ThermalControllerTest, BoringFactoryTest)
22 {
23     // Verifies building a ThermalPIDController with the factory works as
24     // expected in the boring (uninteresting) case.
25 
26     ZoneMock z;
27 
28     std::vector<std::string> inputs = {"fleeting0"};
29     double setpoint = 10.0;
30     ec::pidinfo initial;
31 
32     std::unique_ptr<PIDController> p = ThermalController::createThermalPid(
33         &z, "therm1", inputs, setpoint, initial, ThermalType::margin);
34     // Success
35     EXPECT_FALSE(p == nullptr);
36 }
37 
38 TEST(ThermalControllerTest, VerifyFactoryFailsWithZeroInputs)
39 {
40     // A thermal controller needs at least one input.
41 
42     ZoneMock z;
43 
44     std::vector<std::string> inputs = {};
45     double setpoint = 10.0;
46     ec::pidinfo initial;
47     std::unique_ptr<PIDController> p;
48     EXPECT_THROW(
49         {
50             p = ThermalController::createThermalPid(
51                 &z, "therm1", inputs, setpoint, initial, ThermalType::margin);
52         },
53         std::exception);
54     EXPECT_TRUE(p == nullptr);
55 }
56 
57 TEST(ThermalControllerTest, InputProc_BehavesAsExpected)
58 {
59     // This test just verifies inputProc behaves as expected.
60 
61     ZoneMock z;
62 
63     std::vector<std::string> inputs = {"fleeting0"};
64     double setpoint = 10.0;
65     ec::pidinfo initial;
66 
67     std::unique_ptr<PIDController> p = ThermalController::createThermalPid(
68         &z, "therm1", inputs, setpoint, initial, ThermalType::margin);
69     EXPECT_FALSE(p == nullptr);
70 
71     EXPECT_CALL(z, getCachedValue(StrEq("fleeting0"))).WillOnce(Return(5.0));
72 
73     EXPECT_EQ(5.0, p->inputProc());
74 }
75 
76 TEST(ThermalControllerTest, SetPtProc_BehavesAsExpected)
77 {
78     // This test just verifies inputProc behaves as expected.
79 
80     ZoneMock z;
81 
82     std::vector<std::string> inputs = {"fleeting0"};
83     double setpoint = 10.0;
84     ec::pidinfo initial;
85 
86     std::unique_ptr<PIDController> p = ThermalController::createThermalPid(
87         &z, "therm1", inputs, setpoint, initial, ThermalType::margin);
88     EXPECT_FALSE(p == nullptr);
89 
90     EXPECT_EQ(setpoint, p->setptProc());
91 }
92 
93 TEST(ThermalControllerTest, OutputProc_BehavesAsExpected)
94 {
95     // This test just verifies outputProc behaves as expected.
96 
97     ZoneMock z;
98 
99     std::vector<std::string> inputs = {"fleeting0"};
100     double setpoint = 10.0;
101     ec::pidinfo initial;
102 
103     std::unique_ptr<PIDController> p = ThermalController::createThermalPid(
104         &z, "therm1", inputs, setpoint, initial, ThermalType::margin);
105     EXPECT_FALSE(p == nullptr);
106 
107     double value = 90.0;
108     EXPECT_CALL(z, addSetPoint(value, "therm1"));
109 
110     p->outputProc(value);
111 }
112 
113 TEST(ThermalControllerTest, InputProc_MultipleInputsAbsolute)
114 {
115     // This test verifies inputProc behaves as expected with multiple absolute
116     // inputs.
117 
118     ZoneMock z;
119 
120     std::vector<std::string> inputs = {"fleeting0", "fleeting1"};
121     double setpoint = 10.0;
122     ec::pidinfo initial;
123 
124     std::unique_ptr<PIDController> p = ThermalController::createThermalPid(
125         &z, "therm1", inputs, setpoint, initial, ThermalType::absolute);
126     EXPECT_FALSE(p == nullptr);
127 
128     EXPECT_CALL(z, getCachedValue(StrEq("fleeting0"))).WillOnce(Return(5.0));
129     EXPECT_CALL(z, getCachedValue(StrEq("fleeting1"))).WillOnce(Return(10.0));
130 
131     EXPECT_EQ(10.0, p->inputProc());
132 }
133 
134 TEST(ThermalControllerTest, InputProc_MultipleInputsMargin)
135 {
136     // This test verifies inputProc behaves as expected with multiple margin
137     // inputs.
138 
139     ZoneMock z;
140 
141     std::vector<std::string> inputs = {"fleeting0", "fleeting1"};
142     double setpoint = 10.0;
143     ec::pidinfo initial;
144 
145     std::unique_ptr<PIDController> p = ThermalController::createThermalPid(
146         &z, "therm1", inputs, setpoint, initial, ThermalType::margin);
147     EXPECT_FALSE(p == nullptr);
148 
149     EXPECT_CALL(z, getCachedValue(StrEq("fleeting0"))).WillOnce(Return(5.0));
150     EXPECT_CALL(z, getCachedValue(StrEq("fleeting1"))).WillOnce(Return(10.0));
151 
152     EXPECT_EQ(5.0, p->inputProc());
153 }
154 
155 TEST(ThermalControllerTest, InputProc_MultipleInputsSummation)
156 {
157     // This test verifies inputProc behaves as expected with multiple summation
158     // inputs.
159 
160     ZoneMock z;
161 
162     std::vector<std::string> inputs = {"fleeting0", "fleeting1"};
163     double setpoint = 10.0;
164     ec::pidinfo initial;
165 
166     std::unique_ptr<PIDController> p = ThermalController::createThermalPid(
167         &z, "therm1", inputs, setpoint, initial, ThermalType::summation);
168     EXPECT_FALSE(p == nullptr);
169 
170     EXPECT_CALL(z, getCachedValue(StrEq("fleeting0"))).WillOnce(Return(5.0));
171     EXPECT_CALL(z, getCachedValue(StrEq("fleeting1"))).WillOnce(Return(10.0));
172 
173     EXPECT_EQ(15.0, p->inputProc());
174 }
175 
176 TEST(ThermalControllerTest, NegHysteresis_BehavesAsExpected)
177 {
178 
179     // This test verifies Negative hysteresis behaves as expected by
180     // crossing the setpoint and noticing readings don't change until past the
181     // hysteresis value
182 
183     ZoneMock z;
184 
185     std::vector<std::string> inputs = {"fleeting0"};
186     double setpoint = 10.0;
187     ec::pidinfo initial;
188     initial.negativeHysteresis = 4.0;
189 
190     std::unique_ptr<PIDController> p = ThermalController::createThermalPid(
191         &z, "therm1", inputs, setpoint, initial, ThermalType::margin);
192     EXPECT_FALSE(p == nullptr);
193 
194     EXPECT_CALL(z, getCachedValue(StrEq("fleeting0")))
195         .Times(3)
196         .WillOnce(Return(12.0))
197         .WillOnce(Return(9.0))
198         .WillOnce(Return(7.0));
199 
200     EXPECT_CALL(z, addSetPoint(_, "therm1")).Times(3);
201 
202     std::vector<double> lastReadings = {12.0, 12.0, 7.0};
203     for (auto& reading : lastReadings)
204     {
205         p->process();
206         EXPECT_EQ(p->getLastInput(), reading);
207     }
208 }
209 
210 TEST(ThermalControllerTest, PosHysteresis_BehavesAsExpected)
211 {
212     // This test verifies Positive hysteresis behaves as expected by
213     // crossing the setpoint and noticing readings don't change until past the
214     // hysteresis value
215 
216     ZoneMock z;
217 
218     std::vector<std::string> inputs = {"fleeting0"};
219     double setpoint = 10.0;
220     ec::pidinfo initial;
221     initial.positiveHysteresis = 5.0;
222 
223     std::unique_ptr<PIDController> p = ThermalController::createThermalPid(
224         &z, "therm1", inputs, setpoint, initial, ThermalType::margin);
225     EXPECT_FALSE(p == nullptr);
226 
227     EXPECT_CALL(z, getCachedValue(StrEq("fleeting0")))
228         .Times(3)
229         .WillOnce(Return(8.0))
230         .WillOnce(Return(13.0))
231         .WillOnce(Return(14.0));
232 
233     EXPECT_CALL(z, addSetPoint(_, "therm1")).Times(3);
234 
235     std::vector<double> lastReadings = {8.0, 8.0, 14.0};
236     for (auto& reading : lastReadings)
237     {
238         p->process();
239         EXPECT_EQ(p->getLastInput(), reading);
240     }
241 }
242 
243 } // namespace
244 } // namespace pid_control
245