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, NegHysteresis_BehavesAsExpected)
156 {
157 
158     // This test verifies Negative hysteresis behaves as expected by
159     // crossing the setpoint and noticing readings don't change until past the
160     // hysteresis value
161 
162     ZoneMock z;
163 
164     std::vector<std::string> inputs = {"fleeting0"};
165     double setpoint = 10.0;
166     ec::pidinfo initial;
167     initial.negativeHysteresis = 4.0;
168 
169     std::unique_ptr<PIDController> p = ThermalController::createThermalPid(
170         &z, "therm1", inputs, setpoint, initial, ThermalType::margin);
171     EXPECT_FALSE(p == nullptr);
172 
173     EXPECT_CALL(z, getCachedValue(StrEq("fleeting0")))
174         .Times(3)
175         .WillOnce(Return(12.0))
176         .WillOnce(Return(9.0))
177         .WillOnce(Return(7.0));
178 
179     EXPECT_CALL(z, addSetPoint(_, "therm1")).Times(3);
180 
181     std::vector<double> lastReadings = {12.0, 12.0, 7.0};
182     for (auto& reading : lastReadings)
183     {
184         p->process();
185         EXPECT_EQ(p->getLastInput(), reading);
186     }
187 }
188 
189 TEST(ThermalControllerTest, PosHysteresis_BehavesAsExpected)
190 {
191     // This test verifies Positive hysteresis behaves as expected by
192     // crossing the setpoint and noticing readings don't change until past the
193     // hysteresis value
194 
195     ZoneMock z;
196 
197     std::vector<std::string> inputs = {"fleeting0"};
198     double setpoint = 10.0;
199     ec::pidinfo initial;
200     initial.positiveHysteresis = 5.0;
201 
202     std::unique_ptr<PIDController> p = ThermalController::createThermalPid(
203         &z, "therm1", inputs, setpoint, initial, ThermalType::margin);
204     EXPECT_FALSE(p == nullptr);
205 
206     EXPECT_CALL(z, getCachedValue(StrEq("fleeting0")))
207         .Times(3)
208         .WillOnce(Return(8.0))
209         .WillOnce(Return(13.0))
210         .WillOnce(Return(14.0));
211 
212     EXPECT_CALL(z, addSetPoint(_, "therm1")).Times(3);
213 
214     std::vector<double> lastReadings = {8.0, 8.0, 14.0};
215     for (auto& reading : lastReadings)
216     {
217         p->process();
218         EXPECT_EQ(p->getLastInput(), reading);
219     }
220 }
221 
222 } // namespace
223 } // namespace pid_control
224