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