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     // This test verifies Negative hysteresis behaves as expected by
179     // crossing the setpoint and noticing readings don't change until past the
180     // hysteresis value
181 
182     ZoneMock z;
183 
184     std::vector<std::string> inputs = {"fleeting0"};
185     double setpoint = 10.0;
186     ec::pidinfo initial;
187     initial.negativeHysteresis = 4.0;
188 
189     std::unique_ptr<PIDController> p = ThermalController::createThermalPid(
190         &z, "therm1", inputs, setpoint, initial, ThermalType::margin);
191     EXPECT_FALSE(p == nullptr);
192 
193     EXPECT_CALL(z, getCachedValue(StrEq("fleeting0")))
194         .Times(3)
195         .WillOnce(Return(12.0))
196         .WillOnce(Return(9.0))
197         .WillOnce(Return(7.0));
198 
199     EXPECT_CALL(z, addSetPoint(_, "therm1")).Times(3);
200 
201     std::vector<double> lastReadings = {12.0, 12.0, 7.0};
202     for (auto& reading : lastReadings)
203     {
204         p->process();
205         EXPECT_EQ(p->getLastInput(), reading);
206     }
207 }
208 
209 TEST(ThermalControllerTest, PosHysteresis_BehavesAsExpected)
210 {
211     // This test verifies Positive hysteresis behaves as expected by
212     // crossing the setpoint and noticing readings don't change until past the
213     // hysteresis value
214 
215     ZoneMock z;
216 
217     std::vector<std::string> inputs = {"fleeting0"};
218     double setpoint = 10.0;
219     ec::pidinfo initial;
220     initial.positiveHysteresis = 5.0;
221 
222     std::unique_ptr<PIDController> p = ThermalController::createThermalPid(
223         &z, "therm1", inputs, setpoint, initial, ThermalType::margin);
224     EXPECT_FALSE(p == nullptr);
225 
226     EXPECT_CALL(z, getCachedValue(StrEq("fleeting0")))
227         .Times(3)
228         .WillOnce(Return(8.0))
229         .WillOnce(Return(13.0))
230         .WillOnce(Return(14.0));
231 
232     EXPECT_CALL(z, addSetPoint(_, "therm1")).Times(3);
233 
234     std::vector<double> lastReadings = {8.0, 8.0, 14.0};
235     for (auto& reading : lastReadings)
236     {
237         p->process();
238         EXPECT_EQ(p->getLastInput(), reading);
239     }
240 }
241 
242 } // namespace
243 } // namespace pid_control
244