1 /** 2 * Copyright © 2020 IBM Corporation 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 #include "action.hpp" 17 #include "chassis.hpp" 18 #include "configuration.hpp" 19 #include "device.hpp" 20 #include "i2c_interface.hpp" 21 #include "mock_action.hpp" 22 #include "mock_error_logging.hpp" 23 #include "mock_journal.hpp" 24 #include "mock_sensors.hpp" 25 #include "mock_services.hpp" 26 #include "mocked_i2c_interface.hpp" 27 #include "phase_fault_detection.hpp" 28 #include "pmbus_read_sensor_action.hpp" 29 #include "pmbus_utils.hpp" 30 #include "presence_detection.hpp" 31 #include "rail.hpp" 32 #include "rule.hpp" 33 #include "sensor_monitoring.hpp" 34 #include "sensors.hpp" 35 #include "system.hpp" 36 37 #include <cstdint> 38 #include <memory> 39 #include <optional> 40 #include <string> 41 #include <tuple> 42 #include <utility> 43 #include <vector> 44 45 #include <gmock/gmock.h> 46 #include <gtest/gtest.h> 47 48 using namespace phosphor::power::regulators; 49 using namespace phosphor::power::regulators::pmbus_utils; 50 51 using ::testing::A; 52 using ::testing::Ref; 53 using ::testing::Return; 54 using ::testing::SetArgReferee; 55 using ::testing::Throw; 56 using ::testing::TypedEq; 57 58 /** 59 * Creates the parent objects that normally contain a SensorMonitoring object. 60 * 61 * A SensorMonitoring object is normally contained within a hierarchy of System, 62 * Chassis, Device, and Rail objects. These objects are required in order to 63 * call the execute() method. 64 * 65 * Creates the System, Chassis, Device, and Rail objects. The SensorMonitoring 66 * object is moved into the Rail object. 67 * 68 * @param monitoring SensorMonitoring object to move into object hierarchy 69 * @return Tuple containing pointers the parent objects and the 70 * MockedI2CInterface object. They are all contained within the System 71 * object and will be automatically destructed. 72 */ 73 std::tuple<std::unique_ptr<System>, Chassis*, Device*, i2c::MockedI2CInterface*, 74 Rail*> 75 createParentObjects(std::unique_ptr<SensorMonitoring> monitoring) 76 { 77 // Create Rail that contains SensorMonitoring 78 std::unique_ptr<Configuration> configuration{}; 79 std::unique_ptr<Rail> rail = std::make_unique<Rail>( 80 "vdd", std::move(configuration), std::move(monitoring)); 81 Rail* railPtr = rail.get(); 82 83 // Create mock I2CInterface 84 std::unique_ptr<i2c::MockedI2CInterface> i2cInterface = 85 std::make_unique<i2c::MockedI2CInterface>(); 86 i2c::MockedI2CInterface* i2cInterfacePtr = i2cInterface.get(); 87 88 // Create Device that contains Rail 89 std::unique_ptr<PresenceDetection> presenceDetection{}; 90 std::unique_ptr<Configuration> deviceConfiguration{}; 91 std::unique_ptr<PhaseFaultDetection> phaseFaultDetection{}; 92 std::vector<std::unique_ptr<Rail>> rails{}; 93 rails.emplace_back(std::move(rail)); 94 std::unique_ptr<Device> device = std::make_unique<Device>( 95 "vdd_reg", true, 96 "/xyz/openbmc_project/inventory/system/chassis/motherboard/reg2", 97 std::move(i2cInterface), std::move(presenceDetection), 98 std::move(deviceConfiguration), std::move(phaseFaultDetection), 99 std::move(rails)); 100 Device* devicePtr = device.get(); 101 102 // Create Chassis that contains Device 103 std::vector<std::unique_ptr<Device>> devices{}; 104 devices.emplace_back(std::move(device)); 105 std::unique_ptr<Chassis> chassis = std::make_unique<Chassis>( 106 1, "/xyz/openbmc_project/inventory/system/chassis", std::move(devices)); 107 Chassis* chassisPtr = chassis.get(); 108 109 // Create System that contains Chassis 110 std::vector<std::unique_ptr<Rule>> rules{}; 111 std::vector<std::unique_ptr<Chassis>> chassisVec{}; 112 chassisVec.emplace_back(std::move(chassis)); 113 std::unique_ptr<System> system = 114 std::make_unique<System>(std::move(rules), std::move(chassisVec)); 115 116 return std::make_tuple(std::move(system), chassisPtr, devicePtr, 117 i2cInterfacePtr, railPtr); 118 } 119 120 TEST(SensorMonitoringTests, Constructor) 121 { 122 std::vector<std::unique_ptr<Action>> actions{}; 123 actions.push_back(std::make_unique<MockAction>()); 124 125 SensorMonitoring sensorMonitoring(std::move(actions)); 126 EXPECT_EQ(sensorMonitoring.getActions().size(), 1); 127 } 128 129 TEST(SensorMonitoringTests, ClearErrorHistory) 130 { 131 // Create PMBusReadSensorAction 132 SensorType type{SensorType::iout}; 133 uint8_t command{0x8C}; 134 SensorDataFormat format{SensorDataFormat::linear_11}; 135 std::optional<int8_t> exponent{}; 136 std::unique_ptr<PMBusReadSensorAction> action = 137 std::make_unique<PMBusReadSensorAction>(type, command, format, 138 exponent); 139 140 // Create SensorMonitoring 141 std::vector<std::unique_ptr<Action>> actions{}; 142 actions.emplace_back(std::move(action)); 143 SensorMonitoring* monitoring = new SensorMonitoring(std::move(actions)); 144 145 // Create parent objects that contain SensorMonitoring 146 auto [system, chassis, device, i2cInterface, rail] = 147 createParentObjects(std::unique_ptr<SensorMonitoring>{monitoring}); 148 149 // Set I2CInterface expectations 150 EXPECT_CALL(*i2cInterface, isOpen).WillRepeatedly(Return(true)); 151 EXPECT_CALL(*i2cInterface, read(TypedEq<uint8_t>(0x8C), A<uint16_t&>())) 152 .WillRepeatedly(Throw( 153 i2c::I2CException{"Failed to read word data", "/dev/i2c-1", 0x70})); 154 155 // Create lambda that sets MockServices expectations. The lambda allows us 156 // to set expectations multiple times without duplicate code. 157 auto setExpectations = [](MockServices& services) { 158 // Expect Sensors service to be called 10 times 159 MockSensors& sensors = services.getMockSensors(); 160 EXPECT_CALL(sensors, startRail).Times(10); 161 EXPECT_CALL(sensors, setValue).Times(0); 162 EXPECT_CALL(sensors, endRail(true)).Times(10); 163 164 // Expect Journal service to be called 6 times to log error messages 165 MockJournal& journal = services.getMockJournal(); 166 EXPECT_CALL(journal, logError(A<const std::vector<std::string>&>())) 167 .Times(6); 168 EXPECT_CALL(journal, logError(A<const std::string&>())).Times(6); 169 170 // Expect ErrorLogging service to be called once to log an I2C error 171 MockErrorLogging& errorLogging = services.getMockErrorLogging(); 172 EXPECT_CALL(errorLogging, logI2CError).Times(1); 173 }; 174 175 // Call execute() 10 times to set error history data members 176 { 177 // Create mock services. Set expectations via lambda. 178 MockServices services{}; 179 setExpectations(services); 180 181 for (int i = 1; i <= 10; ++i) 182 { 183 monitoring->execute(services, *system, *chassis, *device, *rail); 184 } 185 } 186 187 // Clear error history 188 monitoring->clearErrorHistory(); 189 190 // Call execute() 10 more times. Should log errors again. 191 { 192 // Create mock services. Set expectations via lambda. 193 MockServices services{}; 194 setExpectations(services); 195 196 for (int i = 1; i <= 10; ++i) 197 { 198 monitoring->execute(services, *system, *chassis, *device, *rail); 199 } 200 } 201 } 202 203 TEST(SensorMonitoringTests, Execute) 204 { 205 // Test where works 206 { 207 // Create PMBusReadSensorAction 208 SensorType type{SensorType::iout}; 209 uint8_t command{0x8C}; 210 SensorDataFormat format{SensorDataFormat::linear_11}; 211 std::optional<int8_t> exponent{}; 212 std::unique_ptr<PMBusReadSensorAction> action = 213 std::make_unique<PMBusReadSensorAction>(type, command, format, 214 exponent); 215 216 // Create SensorMonitoring 217 std::vector<std::unique_ptr<Action>> actions{}; 218 actions.emplace_back(std::move(action)); 219 SensorMonitoring* monitoring = new SensorMonitoring(std::move(actions)); 220 221 // Create parent objects that contain SensorMonitoring 222 auto [system, chassis, device, i2cInterface, rail] = 223 createParentObjects(std::unique_ptr<SensorMonitoring>{monitoring}); 224 225 // Set I2CInterface expectations 226 EXPECT_CALL(*i2cInterface, isOpen).Times(1).WillOnce(Return(true)); 227 EXPECT_CALL(*i2cInterface, read(TypedEq<uint8_t>(0x8C), A<uint16_t&>())) 228 .Times(1) 229 .WillOnce(SetArgReferee<1>(0xD2E0)); 230 231 // Create mock services. Set Sensors service expectations. 232 MockServices services{}; 233 MockSensors& sensors = services.getMockSensors(); 234 EXPECT_CALL(sensors, 235 startRail("vdd", 236 "/xyz/openbmc_project/inventory/system/chassis/" 237 "motherboard/reg2", 238 "/xyz/openbmc_project/inventory/system/chassis")) 239 .Times(1); 240 EXPECT_CALL(sensors, setValue(SensorType::iout, 11.5)).Times(1); 241 EXPECT_CALL(sensors, endRail(false)).Times(1); 242 243 // Execute SensorMonitoring 244 monitoring->execute(services, *system, *chassis, *device, *rail); 245 } 246 247 // Test where fails 248 { 249 // Create PMBusReadSensorAction 250 SensorType type{SensorType::iout}; 251 uint8_t command{0x8C}; 252 SensorDataFormat format{SensorDataFormat::linear_11}; 253 std::optional<int8_t> exponent{}; 254 std::unique_ptr<PMBusReadSensorAction> action = 255 std::make_unique<PMBusReadSensorAction>(type, command, format, 256 exponent); 257 258 // Create SensorMonitoring 259 std::vector<std::unique_ptr<Action>> actions{}; 260 actions.emplace_back(std::move(action)); 261 SensorMonitoring* monitoring = new SensorMonitoring(std::move(actions)); 262 263 // Create parent objects that contain SensorMonitoring 264 auto [system, chassis, device, i2cInterface, rail] = 265 createParentObjects(std::unique_ptr<SensorMonitoring>{monitoring}); 266 267 // Set I2CInterface expectations 268 EXPECT_CALL(*i2cInterface, isOpen).WillRepeatedly(Return(true)); 269 EXPECT_CALL(*i2cInterface, read(TypedEq<uint8_t>(0x8C), A<uint16_t&>())) 270 .WillRepeatedly(Throw(i2c::I2CException{"Failed to read word data", 271 "/dev/i2c-1", 0x70})); 272 273 // Create lambda that sets MockServices expectations. The lambda allows 274 // us to set expectations multiple times without duplicate code. 275 auto setExpectations = [](MockServices& services, int executeCount, 276 int journalCount, int errorLogCount) { 277 // Set Sensors service expectations 278 MockSensors& sensors = services.getMockSensors(); 279 EXPECT_CALL( 280 sensors, 281 startRail("vdd", 282 "/xyz/openbmc_project/inventory/system/chassis/" 283 "motherboard/reg2", 284 "/xyz/openbmc_project/inventory/system/chassis")) 285 .Times(executeCount); 286 EXPECT_CALL(sensors, setValue).Times(0); 287 EXPECT_CALL(sensors, endRail(true)).Times(executeCount); 288 289 // Set Journal service expectations 290 MockJournal& journal = services.getMockJournal(); 291 std::vector<std::string> expectedErrMessagesException{ 292 "I2CException: Failed to read word data: bus /dev/i2c-1, addr 0x70", 293 "ActionError: pmbus_read_sensor: { type: iout, command: 0x8C, " 294 "format: linear_11 }"}; 295 EXPECT_CALL(journal, logError(expectedErrMessagesException)) 296 .Times(journalCount); 297 EXPECT_CALL(journal, 298 logError("Unable to monitor sensors for rail vdd")) 299 .Times(journalCount); 300 301 // Set ErrorLogging service expectations 302 MockErrorLogging& errorLogging = services.getMockErrorLogging(); 303 EXPECT_CALL(errorLogging, 304 logI2CError(Entry::Level::Warning, Ref(journal), 305 "/dev/i2c-1", 0x70, 0)) 306 .Times(errorLogCount); 307 }; 308 309 // Call execute() 5 times. Should log 5 journal messages and create 0 310 // error logs. 311 { 312 // Create mock services. Set expectations via lambda. 313 MockServices services{}; 314 setExpectations(services, 5, 5, 0); 315 316 for (int i = 1; i <= 5; ++i) 317 { 318 monitoring->execute(services, *system, *chassis, *device, 319 *rail); 320 } 321 } 322 323 // Call execute() 1 more time. Should log 1 journal message and create 324 // 1 error log. 325 { 326 // Create mock services. Set expectations via lambda. 327 MockServices services{}; 328 setExpectations(services, 1, 1, 1); 329 330 monitoring->execute(services, *system, *chassis, *device, *rail); 331 } 332 333 // Call execute() 5 more times. Should log 0 journal messages and 334 // create 0 error logs. 335 { 336 // Create mock services. Set expectations via lambda. 337 MockServices services{}; 338 setExpectations(services, 5, 0, 0); 339 340 for (int i = 1; i <= 5; ++i) 341 { 342 monitoring->execute(services, *system, *chassis, *device, 343 *rail); 344 } 345 } 346 } 347 } 348 349 TEST(SensorMonitoringTests, GetActions) 350 { 351 std::vector<std::unique_ptr<Action>> actions{}; 352 353 MockAction* action1 = new MockAction{}; 354 actions.push_back(std::unique_ptr<MockAction>{action1}); 355 356 MockAction* action2 = new MockAction{}; 357 actions.push_back(std::unique_ptr<MockAction>{action2}); 358 359 SensorMonitoring sensorMonitoring(std::move(actions)); 360 EXPECT_EQ(sensorMonitoring.getActions().size(), 2); 361 EXPECT_EQ(sensorMonitoring.getActions()[0].get(), action1); 362 EXPECT_EQ(sensorMonitoring.getActions()[1].get(), action2); 363 } 364