1 #include "../power_supply.hpp" 2 #include "mock.hpp" 3 4 #include <xyz/openbmc_project/Common/Device/error.hpp> 5 #include <xyz/openbmc_project/Common/error.hpp> 6 7 #include <gmock/gmock.h> 8 #include <gtest/gtest.h> 9 10 using namespace phosphor::power::psu; 11 using namespace phosphor::pmbus; 12 13 using ::testing::_; 14 using ::testing::Args; 15 using ::testing::Assign; 16 using ::testing::DoAll; 17 using ::testing::ElementsAre; 18 using ::testing::NotNull; 19 using ::testing::Return; 20 using ::testing::StrEq; 21 22 static auto PSUInventoryPath = "/xyz/bmc/inv/sys/chassis/board/powersupply0"; 23 24 class PowerSupplyTests : public ::testing::Test 25 { 26 public: 27 PowerSupplyTests() : 28 mockedUtil(reinterpret_cast<const MockedUtil&>(getUtils())) 29 { 30 ON_CALL(mockedUtil, getPresence(_, _)).WillByDefault(Return(false)); 31 } 32 33 ~PowerSupplyTests() override 34 { 35 freeUtils(); 36 } 37 38 const MockedUtil& mockedUtil; 39 }; 40 41 TEST_F(PowerSupplyTests, Constructor) 42 { 43 /** 44 * @param[in] invpath - String for inventory path to use 45 * @param[in] i2cbus - The bus number this power supply is on 46 * @param[in] i2caddr - The 16-bit I2C address of the power supply 47 */ 48 auto bus = sdbusplus::bus::new_default(); 49 50 // Try where inventory path is empty, constructor should fail. 51 try 52 { 53 auto psu = std::make_unique<PowerSupply>(bus, "", 3, 0x68); 54 ADD_FAILURE() << "Should not have reached this line."; 55 } 56 catch (const std::invalid_argument& e) 57 { 58 EXPECT_STREQ(e.what(), "Invalid empty inventoryPath"); 59 } 60 catch (...) 61 { 62 ADD_FAILURE() << "Should not have caught exception."; 63 } 64 65 // Test with valid arguments 66 try 67 { 68 EXPECT_CALL(mockedUtil, getPresence(_, StrEq(PSUInventoryPath))) 69 .Times(1); 70 auto psu = 71 std::make_unique<PowerSupply>(bus, PSUInventoryPath, 3, 0x68); 72 73 EXPECT_EQ(psu->isPresent(), false); 74 EXPECT_EQ(psu->isFaulted(), false); 75 EXPECT_EQ(psu->hasInputFault(), false); 76 EXPECT_EQ(psu->hasMFRFault(), false); 77 EXPECT_EQ(psu->hasVINUVFault(), false); 78 } 79 catch (...) 80 { 81 ADD_FAILURE() << "Should not have caught exception."; 82 } 83 } 84 85 TEST_F(PowerSupplyTests, Analyze) 86 { 87 auto bus = sdbusplus::bus::new_default(); 88 89 EXPECT_CALL(mockedUtil, getPresence(_, StrEq(PSUInventoryPath))).Times(1); 90 PowerSupply psu{bus, PSUInventoryPath, 4, 0x69}; 91 psu.analyze(); 92 // By default, nothing should change. 93 EXPECT_EQ(psu.isPresent(), false); 94 EXPECT_EQ(psu.isFaulted(), false); 95 EXPECT_EQ(psu.hasInputFault(), false); 96 EXPECT_EQ(psu.hasMFRFault(), false); 97 EXPECT_EQ(psu.hasVINUVFault(), false); 98 99 // In order to get the various faults tested, the power supply needs to be 100 // present in order to read from the PMBus device(s). 101 EXPECT_CALL(mockedUtil, getPresence(_, StrEq(PSUInventoryPath))) 102 .Times(1) 103 .WillOnce(Return(true)); // present 104 PowerSupply psu2{bus, PSUInventoryPath, 5, 0x6a}; 105 EXPECT_EQ(psu2.isPresent(), true); 106 107 // STATUS_WORD 0x0000 is powered on, no faults. 108 MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu2.getPMBus()); 109 EXPECT_CALL(mockPMBus, read(_, _)).Times(1).WillOnce(Return(0x0000)); 110 psu2.analyze(); 111 EXPECT_EQ(psu2.isPresent(), true); 112 EXPECT_EQ(psu2.isFaulted(), false); 113 EXPECT_EQ(psu2.hasInputFault(), false); 114 EXPECT_EQ(psu2.hasMFRFault(), false); 115 EXPECT_EQ(psu2.hasVINUVFault(), false); 116 117 // STATUS_WORD input fault/warn 118 EXPECT_CALL(mockPMBus, read(_, _)) 119 .Times(2) 120 .WillOnce(Return(status_word::INPUT_FAULT_WARN)) 121 .WillOnce(Return(0x0000)); 122 psu2.analyze(); 123 EXPECT_EQ(psu2.isPresent(), true); 124 EXPECT_EQ(psu2.isFaulted(), true); 125 EXPECT_EQ(psu2.hasInputFault(), true); 126 EXPECT_EQ(psu2.hasMFRFault(), false); 127 EXPECT_EQ(psu2.hasVINUVFault(), false); 128 129 // STATUS_WORD INPUT/UV fault. 130 // First need it to return good status, then the fault 131 EXPECT_CALL(mockPMBus, read(_, _)) 132 .WillOnce(Return(0x0000)) 133 .WillOnce(Return(status_word::VIN_UV_FAULT)) 134 .WillOnce(Return(0x0000)); 135 psu2.analyze(); 136 psu2.analyze(); 137 EXPECT_EQ(psu2.isPresent(), true); 138 EXPECT_EQ(psu2.isFaulted(), true); 139 EXPECT_EQ(psu2.hasInputFault(), false); 140 EXPECT_EQ(psu2.hasMFRFault(), false); 141 EXPECT_EQ(psu2.hasVINUVFault(), true); 142 143 // STATUS_WORD MFR fault. 144 EXPECT_CALL(mockPMBus, read(_, _)) 145 .WillOnce(Return(0x0000)) 146 .WillOnce(Return(status_word::MFR_SPECIFIC_FAULT)) 147 .WillOnce(Return(1)); // mock return for read(STATUS_MFR... ) 148 psu2.analyze(); 149 psu2.analyze(); 150 EXPECT_EQ(psu2.isPresent(), true); 151 EXPECT_EQ(psu2.isFaulted(), true); 152 EXPECT_EQ(psu2.hasInputFault(), false); 153 EXPECT_EQ(psu2.hasMFRFault(), true); 154 EXPECT_EQ(psu2.hasVINUVFault(), false); 155 156 // Ignore Temperature fault. 157 EXPECT_CALL(mockPMBus, read(_, _)) 158 .WillOnce(Return(0x0000)) 159 .WillOnce(Return(status_word::TEMPERATURE_FAULT_WARN)) 160 .WillOnce(Return(0x0000)); 161 psu2.analyze(); 162 psu2.analyze(); 163 EXPECT_EQ(psu2.isPresent(), true); 164 EXPECT_EQ(psu2.isFaulted(), false); 165 EXPECT_EQ(psu2.hasInputFault(), false); 166 EXPECT_EQ(psu2.hasMFRFault(), false); 167 EXPECT_EQ(psu2.hasVINUVFault(), false); 168 169 // Ignore fan fault 170 EXPECT_CALL(mockPMBus, read(_, _)) 171 .WillOnce(Return(0x0000)) 172 .WillOnce(Return(status_word::FAN_FAULT)) 173 .WillOnce(Return(0x0000)); 174 psu2.analyze(); 175 psu2.analyze(); 176 EXPECT_EQ(psu2.isPresent(), true); 177 EXPECT_EQ(psu2.isFaulted(), false); 178 EXPECT_EQ(psu2.hasInputFault(), false); 179 EXPECT_EQ(psu2.hasMFRFault(), false); 180 EXPECT_EQ(psu2.hasVINUVFault(), false); 181 182 // TODO: ReadFailure 183 } 184 185 TEST_F(PowerSupplyTests, OnOffConfig) 186 { 187 auto bus = sdbusplus::bus::new_default(); 188 uint8_t data = 0x15; 189 190 // Test where PSU is NOT present 191 try 192 { 193 EXPECT_CALL(mockedUtil, getPresence(_, StrEq(PSUInventoryPath))) 194 .Times(1) 195 .WillOnce(Return(false)); 196 PowerSupply psu{bus, PSUInventoryPath, 4, 0x69}; 197 MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus()); 198 // If it is not present, I should not be trying to write to it. 199 EXPECT_CALL(mockPMBus, writeBinary(_, _, _)).Times(0); 200 psu.onOffConfig(data); 201 } 202 catch (...) 203 { 204 } 205 206 // Test where PSU is present 207 try 208 { 209 EXPECT_CALL(mockedUtil, getPresence(_, StrEq(PSUInventoryPath))) 210 .Times(1) 211 .WillOnce(Return(true)); // present 212 PowerSupply psu{bus, PSUInventoryPath, 5, 0x6a}; 213 MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus()); 214 // TODO: ???should I check the filename? 215 EXPECT_CALL(mockPMBus, 216 writeBinary(_, ElementsAre(0x15), Type::HwmonDeviceDebug)) 217 .Times(1); 218 psu.onOffConfig(data); 219 } 220 catch (...) 221 { 222 } 223 } 224 225 TEST_F(PowerSupplyTests, ClearFaults) 226 { 227 auto bus = sdbusplus::bus::new_default(); 228 EXPECT_CALL(mockedUtil, getPresence(_, StrEq(PSUInventoryPath))) 229 .Times(1) 230 .WillOnce(Return(true)); // present 231 PowerSupply psu{bus, PSUInventoryPath, 13, 0x68}; 232 EXPECT_EQ(psu.isPresent(), true); 233 EXPECT_EQ(psu.isFaulted(), false); 234 EXPECT_EQ(psu.hasInputFault(), false); 235 EXPECT_EQ(psu.hasMFRFault(), false); 236 EXPECT_EQ(psu.hasVINUVFault(), false); 237 MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus()); 238 EXPECT_CALL(mockPMBus, read(_, _)) 239 .Times(2) 240 .WillOnce(Return(0xFFFF)) 241 .WillOnce(Return(1)); // mock return for read(STATUS_MFR... ) 242 psu.analyze(); 243 EXPECT_EQ(psu.isPresent(), true); 244 EXPECT_EQ(psu.isFaulted(), true); 245 EXPECT_EQ(psu.hasInputFault(), true); 246 EXPECT_EQ(psu.hasMFRFault(), true); 247 EXPECT_EQ(psu.hasVINUVFault(), true); 248 EXPECT_CALL(mockPMBus, read("in1_input", _)) 249 .Times(1) 250 .WillOnce(Return(209000)); 251 psu.clearFaults(); 252 EXPECT_EQ(psu.isPresent(), true); 253 EXPECT_EQ(psu.isFaulted(), false); 254 EXPECT_EQ(psu.hasInputFault(), false); 255 EXPECT_EQ(psu.hasMFRFault(), false); 256 EXPECT_EQ(psu.hasVINUVFault(), false); 257 } 258 259 TEST_F(PowerSupplyTests, UpdateInventory) 260 { 261 auto bus = sdbusplus::bus::new_default(); 262 263 try 264 { 265 EXPECT_CALL(mockedUtil, getPresence(_, StrEq(PSUInventoryPath))) 266 .Times(1) 267 .WillOnce(Return(false)); // missing 268 PowerSupply psu{bus, PSUInventoryPath, 3, 0x68}; 269 MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus()); 270 // If it is not present, I should not be trying to read a string 271 EXPECT_CALL(mockPMBus, readString(_, _)).Times(0); 272 psu.updateInventory(); 273 } 274 catch (...) 275 { 276 ADD_FAILURE() << "Should not have caught exception."; 277 } 278 279 try 280 { 281 EXPECT_CALL(mockedUtil, getPresence(_, StrEq(PSUInventoryPath))) 282 .Times(1) 283 .WillOnce(Return(true)); // present 284 PowerSupply psu{bus, PSUInventoryPath, 13, 0x69}; 285 MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus()); 286 EXPECT_CALL(mockPMBus, readString(_, _)).WillRepeatedly(Return("")); 287 psu.updateInventory(); 288 289 EXPECT_CALL(mockPMBus, readString(_, _)) 290 .WillOnce(Return("CCIN")) 291 .WillOnce(Return("PN3456")) 292 .WillOnce(Return("FN3456")) 293 .WillOnce(Return("HEADER")) 294 .WillOnce(Return("SN3456")) 295 .WillOnce(Return("FW3456")); 296 psu.updateInventory(); 297 // TODO: D-Bus mocking to verify values stored on D-Bus (???) 298 } 299 catch (...) 300 { 301 ADD_FAILURE() << "Should not have caught exception."; 302 } 303 } 304 305 TEST_F(PowerSupplyTests, IsPresent) 306 { 307 auto bus = sdbusplus::bus::new_default(); 308 EXPECT_CALL(mockedUtil, getPresence(_, StrEq(PSUInventoryPath))).Times(1); 309 PowerSupply psu{bus, PSUInventoryPath, 3, 0x68}; 310 EXPECT_EQ(psu.isPresent(), false); 311 312 EXPECT_CALL(mockedUtil, getPresence(_, _)) 313 .WillOnce(Return(true)); // present 314 PowerSupply psu2{bus, PSUInventoryPath, 10, 0x6b}; 315 EXPECT_EQ(psu2.isPresent(), true); 316 } 317 318 TEST_F(PowerSupplyTests, IsFaulted) 319 { 320 auto bus = sdbusplus::bus::new_default(); 321 EXPECT_CALL(mockedUtil, getPresence(_, _)) 322 .WillOnce(Return(true)); // present 323 PowerSupply psu{bus, PSUInventoryPath, 11, 0x6f}; 324 EXPECT_EQ(psu.isFaulted(), false); 325 MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus()); 326 EXPECT_CALL(mockPMBus, read(_, _)) 327 .Times(2) 328 .WillOnce(Return(0xFFFF)) 329 .WillOnce(Return(1)); // mock return for read(STATUS_MFR... ) 330 psu.analyze(); 331 EXPECT_EQ(psu.isFaulted(), true); 332 } 333 334 TEST_F(PowerSupplyTests, HasInputFault) 335 { 336 auto bus = sdbusplus::bus::new_default(); 337 EXPECT_CALL(mockedUtil, getPresence(_, _)) 338 .WillOnce(Return(true)); // present 339 PowerSupply psu{bus, PSUInventoryPath, 3, 0x68}; 340 MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus()); 341 EXPECT_EQ(psu.hasInputFault(), false); 342 EXPECT_CALL(mockPMBus, read(_, _)).Times(1).WillOnce(Return(0x0000)); 343 psu.analyze(); 344 EXPECT_EQ(psu.hasInputFault(), false); 345 EXPECT_CALL(mockPMBus, read(_, _)) 346 .Times(2) 347 .WillOnce(Return(status_word::INPUT_FAULT_WARN)) 348 .WillOnce(Return(0)); 349 psu.analyze(); 350 EXPECT_EQ(psu.hasInputFault(), true); 351 EXPECT_CALL(mockPMBus, read(_, _)).Times(1).WillOnce(Return(0x0000)); 352 psu.analyze(); 353 EXPECT_EQ(psu.hasInputFault(), false); 354 } 355 356 TEST_F(PowerSupplyTests, HasMFRFault) 357 { 358 auto bus = sdbusplus::bus::new_default(); 359 EXPECT_CALL(mockedUtil, getPresence(_, _)) 360 .WillOnce(Return(true)); // present 361 PowerSupply psu{bus, PSUInventoryPath, 3, 0x68}; 362 MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus()); 363 EXPECT_EQ(psu.hasMFRFault(), false); 364 EXPECT_CALL(mockPMBus, read(_, _)).Times(1).WillOnce(Return(0x0000)); 365 psu.analyze(); 366 EXPECT_EQ(psu.hasMFRFault(), false); 367 EXPECT_CALL(mockPMBus, read(_, _)) 368 .Times(2) 369 .WillOnce(Return(status_word::MFR_SPECIFIC_FAULT)) 370 .WillOnce(Return(1)); // mock return for read(STATUS_MFR... ) 371 psu.analyze(); 372 EXPECT_EQ(psu.hasMFRFault(), true); 373 EXPECT_CALL(mockPMBus, read(_, _)).Times(1).WillOnce(Return(0x0000)); 374 psu.analyze(); 375 EXPECT_EQ(psu.hasMFRFault(), false); 376 } 377 378 TEST_F(PowerSupplyTests, HasVINUVFault) 379 { 380 auto bus = sdbusplus::bus::new_default(); 381 EXPECT_CALL(mockedUtil, getPresence(_, _)) 382 .WillOnce(Return(true)); // present 383 PowerSupply psu{bus, PSUInventoryPath, 3, 0x68}; 384 MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus()); 385 EXPECT_EQ(psu.hasVINUVFault(), false); 386 EXPECT_CALL(mockPMBus, read(_, _)).Times(1).WillOnce(Return(0x0000)); 387 psu.analyze(); 388 EXPECT_EQ(psu.hasVINUVFault(), false); 389 EXPECT_CALL(mockPMBus, read(_, _)) 390 .Times(2) 391 .WillOnce(Return(status_word::VIN_UV_FAULT)) 392 .WillOnce(Return(0)); 393 psu.analyze(); 394 EXPECT_EQ(psu.hasVINUVFault(), true); 395 EXPECT_CALL(mockPMBus, read(_, _)).Times(1).WillOnce(Return(0x0000)); 396 psu.analyze(); 397 EXPECT_EQ(psu.hasVINUVFault(), false); 398 } 399