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 static auto PSUGPIOLineName = "presence-ps0"; 24 25 class PowerSupplyTests : public ::testing::Test 26 { 27 public: 28 PowerSupplyTests() : 29 mockedUtil(reinterpret_cast<const MockedUtil&>(getUtils())) 30 { 31 ON_CALL(mockedUtil, getPresence(_, _)).WillByDefault(Return(false)); 32 } 33 34 ~PowerSupplyTests() override 35 { 36 freeUtils(); 37 } 38 39 const MockedUtil& mockedUtil; 40 }; 41 42 TEST_F(PowerSupplyTests, Constructor) 43 { 44 /** 45 * @param[in] invpath - String for inventory path to use 46 * @param[in] i2cbus - The bus number this power supply is on 47 * @param[in] i2caddr - The 16-bit I2C address of the power supply 48 * @param[in] gpioLineName - The string for the gpio-line-name to read for 49 * presence. 50 * @param[in] bindDelay - Time in milliseconds to delay binding the device 51 * driver after seeing the presence line go active. 52 */ 53 auto bus = sdbusplus::bus::new_default(); 54 55 // Try where inventory path is empty, constructor should fail. 56 try 57 { 58 auto psu = 59 std::make_unique<PowerSupply>(bus, "", 3, 0x68, PSUGPIOLineName); 60 ADD_FAILURE() << "Should not have reached this line."; 61 } 62 catch (const std::invalid_argument& e) 63 { 64 EXPECT_STREQ(e.what(), "Invalid empty inventoryPath"); 65 } 66 catch (...) 67 { 68 ADD_FAILURE() << "Should not have caught exception."; 69 } 70 71 // TODO: Try invalid i2c address? 72 73 // Try where gpioLineName is empty. 74 try 75 { 76 auto psu = 77 std::make_unique<PowerSupply>(bus, PSUInventoryPath, 3, 0x68, ""); 78 ADD_FAILURE() 79 << "Should not have reached this line. Invalid gpioLineName."; 80 } 81 catch (const std::invalid_argument& e) 82 { 83 EXPECT_STREQ(e.what(), "Invalid empty gpioLineName"); 84 } 85 catch (...) 86 { 87 ADD_FAILURE() << "Should not have caught exception."; 88 } 89 90 // Test with valid arguments 91 // NOT using D-Bus inventory path for presence. 92 try 93 { 94 auto psu = std::make_unique<PowerSupply>(bus, PSUInventoryPath, 3, 0x68, 95 PSUGPIOLineName); 96 97 EXPECT_EQ(psu->isPresent(), false); 98 EXPECT_EQ(psu->isFaulted(), false); 99 EXPECT_EQ(psu->hasInputFault(), false); 100 EXPECT_EQ(psu->hasMFRFault(), false); 101 EXPECT_EQ(psu->hasVINUVFault(), false); 102 } 103 catch (...) 104 { 105 ADD_FAILURE() << "Should not have caught exception."; 106 } 107 108 // Test with valid arguments 109 // TODO: Using D-Bus inventory path for presence. 110 try 111 { 112 // FIXME: How do I get that presenceGPIO.read() in the startup to throw 113 // an exception? 114 115 // EXPECT_CALL(mockedUtil, getPresence(_, 116 // StrEq(PSUInventoryPath))) 117 // .Times(1); 118 } 119 catch (...) 120 { 121 ADD_FAILURE() << "Should not have caught exception."; 122 } 123 } 124 125 TEST_F(PowerSupplyTests, Analyze) 126 { 127 auto bus = sdbusplus::bus::new_default(); 128 129 // If I default to reading the GPIO, I will NOT expect a call to 130 // getPresence(). 131 132 PowerSupply psu{bus, PSUInventoryPath, 4, 0x69, PSUGPIOLineName}; 133 MockedGPIOReader* mockPresenceGPIO = 134 static_cast<MockedGPIOReader*>(psu.getPresenceGPIO()); 135 EXPECT_CALL(*mockPresenceGPIO, read()).Times(1).WillOnce(Return(0)); 136 137 psu.analyze(); 138 // By default, nothing should change. 139 EXPECT_EQ(psu.isPresent(), false); 140 EXPECT_EQ(psu.isFaulted(), false); 141 EXPECT_EQ(psu.hasInputFault(), false); 142 EXPECT_EQ(psu.hasMFRFault(), false); 143 EXPECT_EQ(psu.hasVINUVFault(), false); 144 145 PowerSupply psu2{bus, PSUInventoryPath, 5, 0x6a, PSUGPIOLineName}; 146 // In order to get the various faults tested, the power supply needs to 147 // be present in order to read from the PMBus device(s). 148 MockedGPIOReader* mockPresenceGPIO2 = 149 static_cast<MockedGPIOReader*>(psu2.getPresenceGPIO()); 150 ON_CALL(*mockPresenceGPIO2, read()).WillByDefault(Return(1)); 151 152 EXPECT_EQ(psu2.isPresent(), false); 153 154 // STATUS_WORD 0x0000 is powered on, no faults. 155 // It will read STATUS_MFR at the same time, so there are 2 reads. 156 MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu2.getPMBus()); 157 EXPECT_CALL(mockPMBus, read(_, _)).Times(2).WillRepeatedly(Return(0x0000)); 158 psu2.analyze(); 159 EXPECT_EQ(psu2.isPresent(), true); 160 EXPECT_EQ(psu2.isFaulted(), false); 161 EXPECT_EQ(psu2.hasInputFault(), false); 162 EXPECT_EQ(psu2.hasMFRFault(), false); 163 EXPECT_EQ(psu2.hasVINUVFault(), false); 164 165 // STATUS_WORD input fault/warn 166 EXPECT_CALL(mockPMBus, read(_, _)) 167 .Times(2) 168 .WillOnce(Return(status_word::INPUT_FAULT_WARN)) 169 .WillOnce(Return(0x0000)); 170 psu2.analyze(); 171 EXPECT_EQ(psu2.isPresent(), true); 172 EXPECT_EQ(psu2.isFaulted(), true); 173 EXPECT_EQ(psu2.hasInputFault(), true); 174 EXPECT_EQ(psu2.hasMFRFault(), false); 175 EXPECT_EQ(psu2.hasVINUVFault(), false); 176 177 // STATUS_WORD INPUT/UV fault. 178 // First need it to return good status, then the fault 179 EXPECT_CALL(mockPMBus, read(_, _)) 180 .WillOnce(Return(0x0000)) 181 .WillOnce(Return(status_word::VIN_UV_FAULT)) 182 .WillOnce(Return(0x0000)); 183 psu2.analyze(); 184 psu2.analyze(); 185 EXPECT_EQ(psu2.isPresent(), true); 186 EXPECT_EQ(psu2.isFaulted(), true); 187 EXPECT_EQ(psu2.hasInputFault(), false); 188 EXPECT_EQ(psu2.hasMFRFault(), false); 189 EXPECT_EQ(psu2.hasVINUVFault(), true); 190 191 // STATUS_WORD MFR fault. 192 EXPECT_CALL(mockPMBus, read(_, _)) 193 .WillOnce(Return(0x0000)) 194 .WillOnce(Return(status_word::MFR_SPECIFIC_FAULT)) 195 .WillOnce(Return(1)); // mock return for read(STATUS_MFR... ) 196 psu2.analyze(); 197 psu2.analyze(); 198 EXPECT_EQ(psu2.isPresent(), true); 199 EXPECT_EQ(psu2.isFaulted(), true); 200 EXPECT_EQ(psu2.hasInputFault(), false); 201 EXPECT_EQ(psu2.hasMFRFault(), true); 202 EXPECT_EQ(psu2.hasVINUVFault(), false); 203 204 // Ignore Temperature fault. 205 EXPECT_CALL(mockPMBus, read(_, _)) 206 .WillOnce(Return(0x0000)) 207 .WillOnce(Return(status_word::TEMPERATURE_FAULT_WARN)) 208 .WillOnce(Return(0x0000)); 209 psu2.analyze(); 210 psu2.analyze(); 211 EXPECT_EQ(psu2.isPresent(), true); 212 EXPECT_EQ(psu2.isFaulted(), false); 213 EXPECT_EQ(psu2.hasInputFault(), false); 214 EXPECT_EQ(psu2.hasMFRFault(), false); 215 EXPECT_EQ(psu2.hasVINUVFault(), false); 216 217 // Ignore fan fault 218 EXPECT_CALL(mockPMBus, read(_, _)) 219 .WillOnce(Return(0x0000)) 220 .WillOnce(Return(status_word::FAN_FAULT)) 221 .WillOnce(Return(0x0000)); 222 psu2.analyze(); 223 psu2.analyze(); 224 EXPECT_EQ(psu2.isPresent(), true); 225 EXPECT_EQ(psu2.isFaulted(), false); 226 EXPECT_EQ(psu2.hasInputFault(), false); 227 EXPECT_EQ(psu2.hasMFRFault(), false); 228 EXPECT_EQ(psu2.hasVINUVFault(), false); 229 230 // TODO: ReadFailure 231 } 232 233 TEST_F(PowerSupplyTests, OnOffConfig) 234 { 235 auto bus = sdbusplus::bus::new_default(); 236 uint8_t data = 0x15; 237 238 // Test where PSU is NOT present 239 try 240 { 241 // Assume GPIO presence, not inventory presence? 242 PowerSupply psu{bus, PSUInventoryPath, 4, 0x69, PSUGPIOLineName}; 243 244 MockedGPIOReader* mockPresenceGPIO = 245 static_cast<MockedGPIOReader*>(psu.getPresenceGPIO()); 246 ON_CALL(*mockPresenceGPIO, read()).WillByDefault(Return(0)); 247 MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus()); 248 // Constructor should set initial presence, default read returns 0. 249 // If it is not present, I should not be trying to write to it. 250 EXPECT_CALL(mockPMBus, writeBinary(_, _, _)).Times(0); 251 psu.onOffConfig(data); 252 } 253 catch (...) 254 { 255 } 256 257 // Test where PSU is present 258 try 259 { 260 // Assume GPIO presence, not inventory presence? 261 PowerSupply psu{bus, PSUInventoryPath, 5, 0x6a, PSUGPIOLineName}; 262 MockedGPIOReader* mockPresenceGPIO = 263 static_cast<MockedGPIOReader*>(psu.getPresenceGPIO()); 264 ON_CALL(*mockPresenceGPIO, read()).WillByDefault(Return(1)); 265 MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus()); 266 // TODO: expect setPresence call? 267 // updatePresence() private function reads gpio, called by analyze(). 268 psu.analyze(); 269 // TODO: ???should I check the filename? 270 EXPECT_CALL(mockPMBus, 271 writeBinary(_, ElementsAre(0x15), Type::HwmonDeviceDebug)) 272 .Times(1); 273 psu.onOffConfig(data); 274 } 275 catch (...) 276 { 277 } 278 } 279 280 TEST_F(PowerSupplyTests, ClearFaults) 281 { 282 auto bus = sdbusplus::bus::new_default(); 283 PowerSupply psu{bus, PSUInventoryPath, 13, 0x68, PSUGPIOLineName}; 284 MockedGPIOReader* mockPresenceGPIO = 285 static_cast<MockedGPIOReader*>(psu.getPresenceGPIO()); 286 // GPIO read return 1 to indicate present. 287 ON_CALL(*mockPresenceGPIO, read()).WillByDefault(Return(1)); 288 MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus()); 289 ON_CALL(mockPMBus, read(_, _)).WillByDefault(Return(0)); 290 psu.analyze(); 291 EXPECT_EQ(psu.isPresent(), true); 292 EXPECT_EQ(psu.isFaulted(), false); 293 EXPECT_EQ(psu.hasInputFault(), false); 294 EXPECT_EQ(psu.hasMFRFault(), false); 295 EXPECT_EQ(psu.hasVINUVFault(), false); 296 EXPECT_CALL(mockPMBus, read(_, _)) 297 .Times(2) 298 .WillOnce(Return(0xFFFF)) 299 .WillOnce(Return(1)); // mock return for read(STATUS_MFR... ) 300 psu.analyze(); 301 EXPECT_EQ(psu.isPresent(), true); 302 EXPECT_EQ(psu.isFaulted(), true); 303 EXPECT_EQ(psu.hasInputFault(), true); 304 EXPECT_EQ(psu.hasMFRFault(), true); 305 EXPECT_EQ(psu.hasVINUVFault(), true); 306 EXPECT_CALL(mockPMBus, read("in1_input", _)) 307 .Times(1) 308 .WillOnce(Return(209000)); 309 psu.clearFaults(); 310 EXPECT_EQ(psu.isPresent(), true); 311 EXPECT_EQ(psu.isFaulted(), false); 312 EXPECT_EQ(psu.hasInputFault(), false); 313 EXPECT_EQ(psu.hasMFRFault(), false); 314 EXPECT_EQ(psu.hasVINUVFault(), false); 315 316 // TODO: Faults clear on missing/present? 317 } 318 319 TEST_F(PowerSupplyTests, UpdateInventory) 320 { 321 auto bus = sdbusplus::bus::new_default(); 322 323 try 324 { 325 PowerSupply psu{bus, PSUInventoryPath, 3, 0x68, PSUGPIOLineName}; 326 MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus()); 327 // If it is not present, I should not be trying to read a string 328 EXPECT_CALL(mockPMBus, readString(_, _)).Times(0); 329 psu.updateInventory(); 330 } 331 catch (...) 332 { 333 ADD_FAILURE() << "Should not have caught exception."; 334 } 335 336 try 337 { 338 PowerSupply psu{bus, PSUInventoryPath, 13, 0x69, PSUGPIOLineName}; 339 MockedGPIOReader* mockPresenceGPIO = 340 static_cast<MockedGPIOReader*>(psu.getPresenceGPIO()); 341 // GPIO read return 1 to indicate present. 342 EXPECT_CALL(*mockPresenceGPIO, read()).Times(1).WillOnce(Return(1)); 343 psu.analyze(); 344 MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus()); 345 EXPECT_CALL(mockPMBus, readString(_, _)).WillRepeatedly(Return("")); 346 psu.updateInventory(); 347 348 #if IBM_VPD 349 EXPECT_CALL(mockPMBus, readString(_, _)) 350 .WillOnce(Return("CCIN")) 351 .WillOnce(Return("PN3456")) 352 .WillOnce(Return("FN3456")) 353 .WillOnce(Return("HEADER")) 354 .WillOnce(Return("SN3456")) 355 .WillOnce(Return("FW3456")); 356 #endif 357 psu.updateInventory(); 358 // TODO: D-Bus mocking to verify values stored on D-Bus (???) 359 } 360 catch (...) 361 { 362 ADD_FAILURE() << "Should not have caught exception."; 363 } 364 } 365 366 TEST_F(PowerSupplyTests, IsPresent) 367 { 368 auto bus = sdbusplus::bus::new_default(); 369 370 PowerSupply psu{bus, PSUInventoryPath, 3, 0x68, PSUGPIOLineName}; 371 MockedGPIOReader* mockPresenceGPIO = 372 static_cast<MockedGPIOReader*>(psu.getPresenceGPIO()); 373 EXPECT_EQ(psu.isPresent(), false); 374 375 // Change GPIO read to return 1 to indicate present. 376 EXPECT_CALL(*mockPresenceGPIO, read()).Times(1).WillOnce(Return(1)); 377 psu.analyze(); 378 EXPECT_EQ(psu.isPresent(), true); 379 } 380 381 TEST_F(PowerSupplyTests, IsFaulted) 382 { 383 auto bus = sdbusplus::bus::new_default(); 384 385 PowerSupply psu{bus, PSUInventoryPath, 11, 0x6f, PSUGPIOLineName}; 386 MockedGPIOReader* mockPresenceGPIO = 387 static_cast<MockedGPIOReader*>(psu.getPresenceGPIO()); 388 // Always return 1 to indicate present. 389 EXPECT_CALL(*mockPresenceGPIO, read()).WillRepeatedly(Return(1)); 390 psu.analyze(); 391 EXPECT_EQ(psu.isFaulted(), false); 392 MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus()); 393 EXPECT_CALL(mockPMBus, read(_, _)) 394 .Times(2) 395 .WillOnce(Return(0xFFFF)) 396 .WillOnce(Return(1)); // mock return for read(STATUS_MFR... ) 397 psu.analyze(); 398 EXPECT_EQ(psu.isFaulted(), true); 399 } 400 401 TEST_F(PowerSupplyTests, HasInputFault) 402 { 403 auto bus = sdbusplus::bus::new_default(); 404 405 PowerSupply psu{bus, PSUInventoryPath, 3, 0x68, PSUGPIOLineName}; 406 MockedGPIOReader* mockPresenceGPIO = 407 static_cast<MockedGPIOReader*>(psu.getPresenceGPIO()); 408 // Always return 1 to indicate present. 409 EXPECT_CALL(*mockPresenceGPIO, read()).WillRepeatedly(Return(1)); 410 psu.analyze(); 411 MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus()); 412 EXPECT_EQ(psu.hasInputFault(), false); 413 EXPECT_CALL(mockPMBus, read(_, _)).Times(1).WillOnce(Return(0x0000)); 414 psu.analyze(); 415 EXPECT_EQ(psu.hasInputFault(), false); 416 EXPECT_CALL(mockPMBus, read(_, _)) 417 .Times(2) 418 .WillOnce(Return(status_word::INPUT_FAULT_WARN)) 419 .WillOnce(Return(0)); 420 psu.analyze(); 421 EXPECT_EQ(psu.hasInputFault(), true); 422 EXPECT_CALL(mockPMBus, read(_, _)).Times(1).WillOnce(Return(0x0000)); 423 psu.analyze(); 424 EXPECT_EQ(psu.hasInputFault(), false); 425 } 426 427 TEST_F(PowerSupplyTests, HasMFRFault) 428 { 429 auto bus = sdbusplus::bus::new_default(); 430 431 PowerSupply psu{bus, PSUInventoryPath, 3, 0x68, PSUGPIOLineName}; 432 MockedGPIOReader* mockPresenceGPIO = 433 static_cast<MockedGPIOReader*>(psu.getPresenceGPIO()); 434 // Always return 1 to indicate present. 435 EXPECT_CALL(*mockPresenceGPIO, read()).WillRepeatedly(Return(1)); 436 psu.analyze(); 437 MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus()); 438 EXPECT_EQ(psu.hasMFRFault(), false); 439 EXPECT_CALL(mockPMBus, read(_, _)).Times(1).WillOnce(Return(0x0000)); 440 psu.analyze(); 441 EXPECT_EQ(psu.hasMFRFault(), false); 442 EXPECT_CALL(mockPMBus, read(_, _)) 443 .Times(2) 444 .WillOnce(Return(status_word::MFR_SPECIFIC_FAULT)) 445 .WillOnce(Return(1)); // mock return for read(STATUS_MFR... ) 446 psu.analyze(); 447 EXPECT_EQ(psu.hasMFRFault(), true); 448 EXPECT_CALL(mockPMBus, read(_, _)).Times(1).WillOnce(Return(0x0000)); 449 psu.analyze(); 450 EXPECT_EQ(psu.hasMFRFault(), false); 451 } 452 453 TEST_F(PowerSupplyTests, HasVINUVFault) 454 { 455 auto bus = sdbusplus::bus::new_default(); 456 457 PowerSupply psu{bus, PSUInventoryPath, 3, 0x68, PSUGPIOLineName}; 458 MockedGPIOReader* mockPresenceGPIO = 459 static_cast<MockedGPIOReader*>(psu.getPresenceGPIO()); 460 // Always return 1 to indicate present. 461 EXPECT_CALL(*mockPresenceGPIO, read()).WillRepeatedly(Return(1)); 462 psu.analyze(); 463 MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus()); 464 EXPECT_EQ(psu.hasVINUVFault(), false); 465 EXPECT_CALL(mockPMBus, read(_, _)).Times(1).WillOnce(Return(0x0000)); 466 psu.analyze(); 467 EXPECT_EQ(psu.hasVINUVFault(), false); 468 EXPECT_CALL(mockPMBus, read(_, _)) 469 .Times(2) 470 .WillOnce(Return(status_word::VIN_UV_FAULT)) 471 .WillOnce(Return(0)); 472 psu.analyze(); 473 EXPECT_EQ(psu.hasVINUVFault(), true); 474 EXPECT_CALL(mockPMBus, read(_, _)).Times(1).WillOnce(Return(0x0000)); 475 psu.analyze(); 476 EXPECT_EQ(psu.hasVINUVFault(), false); 477 } 478