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