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 #if IBM_VPD 290 EXPECT_CALL(mockPMBus, readString(_, _)) 291 .WillOnce(Return("CCIN")) 292 .WillOnce(Return("PN3456")) 293 .WillOnce(Return("FN3456")) 294 .WillOnce(Return("HEADER")) 295 .WillOnce(Return("SN3456")) 296 .WillOnce(Return("FW3456")); 297 #endif 298 psu.updateInventory(); 299 // TODO: D-Bus mocking to verify values stored on D-Bus (???) 300 } 301 catch (...) 302 { 303 ADD_FAILURE() << "Should not have caught exception."; 304 } 305 } 306 307 TEST_F(PowerSupplyTests, IsPresent) 308 { 309 auto bus = sdbusplus::bus::new_default(); 310 EXPECT_CALL(mockedUtil, getPresence(_, StrEq(PSUInventoryPath))).Times(1); 311 PowerSupply psu{bus, PSUInventoryPath, 3, 0x68}; 312 EXPECT_EQ(psu.isPresent(), false); 313 314 EXPECT_CALL(mockedUtil, getPresence(_, _)) 315 .WillOnce(Return(true)); // present 316 PowerSupply psu2{bus, PSUInventoryPath, 10, 0x6b}; 317 EXPECT_EQ(psu2.isPresent(), true); 318 } 319 320 TEST_F(PowerSupplyTests, IsFaulted) 321 { 322 auto bus = sdbusplus::bus::new_default(); 323 EXPECT_CALL(mockedUtil, getPresence(_, _)) 324 .WillOnce(Return(true)); // present 325 PowerSupply psu{bus, PSUInventoryPath, 11, 0x6f}; 326 EXPECT_EQ(psu.isFaulted(), false); 327 MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus()); 328 EXPECT_CALL(mockPMBus, read(_, _)) 329 .Times(2) 330 .WillOnce(Return(0xFFFF)) 331 .WillOnce(Return(1)); // mock return for read(STATUS_MFR... ) 332 psu.analyze(); 333 EXPECT_EQ(psu.isFaulted(), true); 334 } 335 336 TEST_F(PowerSupplyTests, HasInputFault) 337 { 338 auto bus = sdbusplus::bus::new_default(); 339 EXPECT_CALL(mockedUtil, getPresence(_, _)) 340 .WillOnce(Return(true)); // present 341 PowerSupply psu{bus, PSUInventoryPath, 3, 0x68}; 342 MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus()); 343 EXPECT_EQ(psu.hasInputFault(), false); 344 EXPECT_CALL(mockPMBus, read(_, _)).Times(1).WillOnce(Return(0x0000)); 345 psu.analyze(); 346 EXPECT_EQ(psu.hasInputFault(), false); 347 EXPECT_CALL(mockPMBus, read(_, _)) 348 .Times(2) 349 .WillOnce(Return(status_word::INPUT_FAULT_WARN)) 350 .WillOnce(Return(0)); 351 psu.analyze(); 352 EXPECT_EQ(psu.hasInputFault(), true); 353 EXPECT_CALL(mockPMBus, read(_, _)).Times(1).WillOnce(Return(0x0000)); 354 psu.analyze(); 355 EXPECT_EQ(psu.hasInputFault(), false); 356 } 357 358 TEST_F(PowerSupplyTests, HasMFRFault) 359 { 360 auto bus = sdbusplus::bus::new_default(); 361 EXPECT_CALL(mockedUtil, getPresence(_, _)) 362 .WillOnce(Return(true)); // present 363 PowerSupply psu{bus, PSUInventoryPath, 3, 0x68}; 364 MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus()); 365 EXPECT_EQ(psu.hasMFRFault(), false); 366 EXPECT_CALL(mockPMBus, read(_, _)).Times(1).WillOnce(Return(0x0000)); 367 psu.analyze(); 368 EXPECT_EQ(psu.hasMFRFault(), false); 369 EXPECT_CALL(mockPMBus, read(_, _)) 370 .Times(2) 371 .WillOnce(Return(status_word::MFR_SPECIFIC_FAULT)) 372 .WillOnce(Return(1)); // mock return for read(STATUS_MFR... ) 373 psu.analyze(); 374 EXPECT_EQ(psu.hasMFRFault(), true); 375 EXPECT_CALL(mockPMBus, read(_, _)).Times(1).WillOnce(Return(0x0000)); 376 psu.analyze(); 377 EXPECT_EQ(psu.hasMFRFault(), false); 378 } 379 380 TEST_F(PowerSupplyTests, HasVINUVFault) 381 { 382 auto bus = sdbusplus::bus::new_default(); 383 EXPECT_CALL(mockedUtil, getPresence(_, _)) 384 .WillOnce(Return(true)); // present 385 PowerSupply psu{bus, PSUInventoryPath, 3, 0x68}; 386 MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus()); 387 EXPECT_EQ(psu.hasVINUVFault(), false); 388 EXPECT_CALL(mockPMBus, read(_, _)).Times(1).WillOnce(Return(0x0000)); 389 psu.analyze(); 390 EXPECT_EQ(psu.hasVINUVFault(), false); 391 EXPECT_CALL(mockPMBus, read(_, _)) 392 .Times(2) 393 .WillOnce(Return(status_word::VIN_UV_FAULT)) 394 .WillOnce(Return(0)); 395 psu.analyze(); 396 EXPECT_EQ(psu.hasVINUVFault(), true); 397 EXPECT_CALL(mockPMBus, read(_, _)).Times(1).WillOnce(Return(0x0000)); 398 psu.analyze(); 399 EXPECT_EQ(psu.hasVINUVFault(), false); 400 } 401