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 struct PMBusExpectations 26 { 27 uint16_t statusWordValue{0x0000}; 28 uint8_t statusInputValue{0x00}; 29 uint8_t statusMFRValue{0x00}; 30 uint8_t statusCMLValue{0x00}; 31 uint8_t statusVOUTValue{0x00}; 32 }; 33 34 // Helper function to setup expectations for various STATUS_* commands 35 void setPMBusExpectations(MockedPMBus& mockPMBus, 36 const PMBusExpectations& expectations) 37 { 38 EXPECT_CALL(mockPMBus, read(STATUS_WORD, _)) 39 .Times(1) 40 .WillOnce(Return(expectations.statusWordValue)); 41 42 if (expectations.statusWordValue != 0) 43 { 44 // If fault bits are on in STATUS_WORD, there will also be a read of 45 // STATUS_INPUT, STATUS_MFR, and STATUS_CML. 46 EXPECT_CALL(mockPMBus, read(STATUS_INPUT, _)) 47 .Times(1) 48 .WillOnce(Return(expectations.statusInputValue)); 49 EXPECT_CALL(mockPMBus, read(STATUS_MFR, _)) 50 .Times(1) 51 .WillOnce(Return(expectations.statusMFRValue)); 52 EXPECT_CALL(mockPMBus, read(STATUS_CML, _)) 53 .Times(1) 54 .WillOnce(Return(expectations.statusCMLValue)); 55 // Page will need to be set to 0 to read STATUS_VOUT. 56 EXPECT_CALL(mockPMBus, insertPageNum(STATUS_VOUT, 0)) 57 .Times(1) 58 .WillOnce(Return("status0_vout")); 59 EXPECT_CALL(mockPMBus, read("status0_vout", _)) 60 .Times(1) 61 .WillOnce(Return(expectations.statusVOUTValue)); 62 } 63 } 64 65 class PowerSupplyTests : public ::testing::Test 66 { 67 public: 68 PowerSupplyTests() : 69 mockedUtil(reinterpret_cast<const MockedUtil&>(getUtils())) 70 { 71 ON_CALL(mockedUtil, getPresence(_, _)).WillByDefault(Return(false)); 72 } 73 74 ~PowerSupplyTests() override 75 { 76 freeUtils(); 77 } 78 79 const MockedUtil& mockedUtil; 80 }; 81 82 TEST_F(PowerSupplyTests, Constructor) 83 { 84 /** 85 * @param[in] invpath - String for inventory path to use 86 * @param[in] i2cbus - The bus number this power supply is on 87 * @param[in] i2caddr - The 16-bit I2C address of the power supply 88 * @param[in] gpioLineName - The string for the gpio-line-name to read for 89 * presence. 90 * @param[in] bindDelay - Time in milliseconds to delay binding the device 91 * driver after seeing the presence line go active. 92 */ 93 auto bus = sdbusplus::bus::new_default(); 94 95 // Try where inventory path is empty, constructor should fail. 96 try 97 { 98 auto psu = 99 std::make_unique<PowerSupply>(bus, "", 3, 0x68, PSUGPIOLineName); 100 ADD_FAILURE() << "Should not have reached this line."; 101 } 102 catch (const std::invalid_argument& e) 103 { 104 EXPECT_STREQ(e.what(), "Invalid empty inventoryPath"); 105 } 106 catch (...) 107 { 108 ADD_FAILURE() << "Should not have caught exception."; 109 } 110 111 // TODO: Try invalid i2c address? 112 113 // Try where gpioLineName is empty. 114 try 115 { 116 auto psu = 117 std::make_unique<PowerSupply>(bus, PSUInventoryPath, 3, 0x68, ""); 118 ADD_FAILURE() 119 << "Should not have reached this line. Invalid gpioLineName."; 120 } 121 catch (const std::invalid_argument& e) 122 { 123 EXPECT_STREQ(e.what(), "Invalid empty gpioLineName"); 124 } 125 catch (...) 126 { 127 ADD_FAILURE() << "Should not have caught exception."; 128 } 129 130 // Test with valid arguments 131 // NOT using D-Bus inventory path for presence. 132 try 133 { 134 auto psu = std::make_unique<PowerSupply>(bus, PSUInventoryPath, 3, 0x68, 135 PSUGPIOLineName); 136 137 EXPECT_EQ(psu->isPresent(), false); 138 EXPECT_EQ(psu->isFaulted(), false); 139 EXPECT_EQ(psu->hasCommFault(), false); 140 EXPECT_EQ(psu->hasInputFault(), false); 141 EXPECT_EQ(psu->hasMFRFault(), false); 142 EXPECT_EQ(psu->hasVINUVFault(), false); 143 EXPECT_EQ(psu->hasVoutOVFault(), false); 144 } 145 catch (...) 146 { 147 ADD_FAILURE() << "Should not have caught exception."; 148 } 149 150 // Test with valid arguments 151 // TODO: Using D-Bus inventory path for presence. 152 try 153 { 154 // FIXME: How do I get that presenceGPIO.read() in the startup to throw 155 // an exception? 156 157 // EXPECT_CALL(mockedUtil, getPresence(_, 158 // StrEq(PSUInventoryPath))) 159 // .Times(1); 160 } 161 catch (...) 162 { 163 ADD_FAILURE() << "Should not have caught exception."; 164 } 165 } 166 167 TEST_F(PowerSupplyTests, Analyze) 168 { 169 auto bus = sdbusplus::bus::new_default(); 170 171 { 172 // If I default to reading the GPIO, I will NOT expect a call to 173 // getPresence(). 174 175 PowerSupply psu{bus, PSUInventoryPath, 4, 0x69, PSUGPIOLineName}; 176 MockedGPIOInterface* mockPresenceGPIO = 177 static_cast<MockedGPIOInterface*>(psu.getPresenceGPIO()); 178 EXPECT_CALL(*mockPresenceGPIO, read()).Times(1).WillOnce(Return(0)); 179 180 psu.analyze(); 181 // By default, nothing should change. 182 EXPECT_EQ(psu.isPresent(), false); 183 EXPECT_EQ(psu.isFaulted(), false); 184 EXPECT_EQ(psu.hasInputFault(), false); 185 EXPECT_EQ(psu.hasMFRFault(), false); 186 EXPECT_EQ(psu.hasVINUVFault(), false); 187 EXPECT_EQ(psu.hasCommFault(), false); 188 EXPECT_EQ(psu.hasVoutOVFault(), false); 189 } 190 191 PowerSupply psu2{bus, PSUInventoryPath, 5, 0x6a, PSUGPIOLineName}; 192 // In order to get the various faults tested, the power supply needs to 193 // be present in order to read from the PMBus device(s). 194 MockedGPIOInterface* mockPresenceGPIO2 = 195 static_cast<MockedGPIOInterface*>(psu2.getPresenceGPIO()); 196 ON_CALL(*mockPresenceGPIO2, read()).WillByDefault(Return(1)); 197 EXPECT_EQ(psu2.isPresent(), false); 198 199 MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu2.getPMBus()); 200 // Presence change from missing to present will trigger write to 201 // ON_OFF_CONFIG. 202 EXPECT_CALL(mockPMBus, writeBinary(ON_OFF_CONFIG, _, _)); 203 // Presence change from missing to present will trigger in1_input read 204 // in an attempt to get CLEAR_FAULTS called. 205 EXPECT_CALL(mockPMBus, read(READ_VIN, _)).Times(1).WillOnce(Return(206000)); 206 207 // STATUS_WORD INPUT fault. 208 { 209 // Start with STATUS_WORD 0x0000. Powered on, no faults. 210 // Set expectations for a no fault 211 PMBusExpectations expectations; 212 setPMBusExpectations(mockPMBus, expectations); 213 psu2.analyze(); 214 EXPECT_EQ(psu2.isPresent(), true); 215 EXPECT_EQ(psu2.isFaulted(), false); 216 EXPECT_EQ(psu2.hasInputFault(), false); 217 EXPECT_EQ(psu2.hasMFRFault(), false); 218 EXPECT_EQ(psu2.hasVINUVFault(), false); 219 EXPECT_EQ(psu2.hasCommFault(), false); 220 EXPECT_EQ(psu2.hasVoutOVFault(), false); 221 222 // Update expectations for STATUS_WORD input fault/warn 223 expectations.statusWordValue = (status_word::INPUT_FAULT_WARN); 224 expectations.statusInputValue = 0x38; 225 setPMBusExpectations(mockPMBus, expectations); 226 psu2.analyze(); 227 EXPECT_EQ(psu2.isPresent(), true); 228 EXPECT_EQ(psu2.isFaulted(), true); 229 EXPECT_EQ(psu2.hasInputFault(), true); 230 EXPECT_EQ(psu2.hasMFRFault(), false); 231 EXPECT_EQ(psu2.hasVINUVFault(), false); 232 EXPECT_EQ(psu2.hasCommFault(), false); 233 EXPECT_EQ(psu2.hasVoutOVFault(), false); 234 } 235 236 // STATUS_WORD INPUT/UV fault. 237 { 238 // First need it to return good status, then the fault 239 PMBusExpectations expectations; 240 setPMBusExpectations(mockPMBus, expectations); 241 psu2.analyze(); 242 // Now set fault bits in STATUS_WORD 243 expectations.statusWordValue = 244 (status_word::INPUT_FAULT_WARN | status_word::VIN_UV_FAULT); 245 // STATUS_INPUT fault bits ... on. 246 expectations.statusInputValue = 0x38; 247 setPMBusExpectations(mockPMBus, expectations); 248 psu2.analyze(); 249 EXPECT_EQ(psu2.isPresent(), true); 250 EXPECT_EQ(psu2.isFaulted(), true); 251 EXPECT_EQ(psu2.hasInputFault(), true); 252 EXPECT_EQ(psu2.hasMFRFault(), false); 253 EXPECT_EQ(psu2.hasVINUVFault(), true); 254 EXPECT_EQ(psu2.hasCommFault(), false); 255 EXPECT_EQ(psu2.hasVoutOVFault(), false); 256 } 257 258 // STATUS_WORD MFR fault. 259 { 260 // First need it to return good status, then the fault 261 PMBusExpectations expectations; 262 setPMBusExpectations(mockPMBus, expectations); 263 psu2.analyze(); 264 // Now STATUS_WORD with MFR fault bit on. 265 expectations.statusWordValue = (status_word::MFR_SPECIFIC_FAULT); 266 // STATUS_MFR bits on. 267 expectations.statusMFRValue = 0xFF; 268 setPMBusExpectations(mockPMBus, expectations); 269 psu2.analyze(); 270 EXPECT_EQ(psu2.isPresent(), true); 271 EXPECT_EQ(psu2.isFaulted(), true); 272 EXPECT_EQ(psu2.hasInputFault(), false); 273 EXPECT_EQ(psu2.hasMFRFault(), true); 274 EXPECT_EQ(psu2.hasVINUVFault(), false); 275 EXPECT_EQ(psu2.hasCommFault(), false); 276 EXPECT_EQ(psu2.hasVoutOVFault(), false); 277 } 278 279 // Ignore Temperature fault. 280 { 281 // First STATUS_WORD with no bits set, then with temperature fault. 282 PMBusExpectations expectations; 283 setPMBusExpectations(mockPMBus, expectations); 284 psu2.analyze(); 285 // STATUS_WORD with temperature fault bit on. 286 expectations.statusWordValue = (status_word::TEMPERATURE_FAULT_WARN); 287 // STATUS_INPUT, STATUS_MFR, STATUS_CML, and STATUS_VOUT fault bits ... 288 // don't care (defaults). 289 setPMBusExpectations(mockPMBus, expectations); 290 psu2.analyze(); 291 EXPECT_EQ(psu2.isPresent(), true); 292 EXPECT_EQ(psu2.isFaulted(), false); 293 EXPECT_EQ(psu2.hasInputFault(), false); 294 EXPECT_EQ(psu2.hasMFRFault(), false); 295 EXPECT_EQ(psu2.hasVINUVFault(), false); 296 EXPECT_EQ(psu2.hasCommFault(), false); 297 EXPECT_EQ(psu2.hasVoutOVFault(), false); 298 } 299 300 // CML fault 301 { 302 // First STATUS_WORD wit no bits set, then with CML fault. 303 PMBusExpectations expectations; 304 setPMBusExpectations(mockPMBus, expectations); 305 psu2.analyze(); 306 // STATUS_WORD with CML fault bit on. 307 expectations.statusWordValue = (status_word::CML_FAULT); 308 // Turn on STATUS_CML fault bit(s) 309 expectations.statusCMLValue = 0xFF; 310 setPMBusExpectations(mockPMBus, expectations); 311 psu2.analyze(); 312 EXPECT_EQ(psu2.isPresent(), true); 313 EXPECT_EQ(psu2.isFaulted(), true); 314 EXPECT_EQ(psu2.hasInputFault(), false); 315 EXPECT_EQ(psu2.hasMFRFault(), false); 316 EXPECT_EQ(psu2.hasVINUVFault(), false); 317 EXPECT_EQ(psu2.hasCommFault(), true); 318 EXPECT_EQ(psu2.hasVoutOVFault(), false); 319 } 320 321 // VOUT_OV_FAULT fault 322 { 323 // First STATUS_WORD with no bits set, then with VOUT/VOUT_OV fault. 324 PMBusExpectations expectations; 325 setPMBusExpectations(mockPMBus, expectations); 326 psu2.analyze(); 327 // STATUS_WORD with VOUT/VOUT_OV fault. 328 expectations.statusWordValue = 329 ((status_word::VOUT_FAULT) | (status_word::VOUT_OV_FAULT)); 330 // Turn on STATUS_VOUT fault bit(s) 331 expectations.statusVOUTValue = 0xA0; 332 setPMBusExpectations(mockPMBus, expectations); 333 psu2.analyze(); 334 EXPECT_EQ(psu2.isPresent(), true); 335 EXPECT_EQ(psu2.isFaulted(), true); 336 EXPECT_EQ(psu2.hasInputFault(), false); 337 EXPECT_EQ(psu2.hasMFRFault(), false); 338 EXPECT_EQ(psu2.hasVINUVFault(), false); 339 EXPECT_EQ(psu2.hasCommFault(), false); 340 EXPECT_EQ(psu2.hasVoutOVFault(), true); 341 } 342 343 // Ignore fan fault 344 { 345 // First STATUS_WORD with no bits set, then with fan fault. 346 PMBusExpectations expectations; 347 setPMBusExpectations(mockPMBus, expectations); 348 psu2.analyze(); 349 expectations.statusWordValue = (status_word::FAN_FAULT); 350 setPMBusExpectations(mockPMBus, expectations); 351 psu2.analyze(); 352 EXPECT_EQ(psu2.isPresent(), true); 353 EXPECT_EQ(psu2.isFaulted(), false); 354 EXPECT_EQ(psu2.hasInputFault(), false); 355 EXPECT_EQ(psu2.hasMFRFault(), false); 356 EXPECT_EQ(psu2.hasVINUVFault(), false); 357 EXPECT_EQ(psu2.hasCommFault(), false); 358 EXPECT_EQ(psu2.hasVoutOVFault(), false); 359 } 360 // TODO: ReadFailure 361 } 362 363 TEST_F(PowerSupplyTests, OnOffConfig) 364 { 365 auto bus = sdbusplus::bus::new_default(); 366 uint8_t data = 0x15; 367 368 // Test where PSU is NOT present 369 try 370 { 371 // Assume GPIO presence, not inventory presence? 372 PowerSupply psu{bus, PSUInventoryPath, 4, 0x69, PSUGPIOLineName}; 373 374 MockedGPIOInterface* mockPresenceGPIO = 375 static_cast<MockedGPIOInterface*>(psu.getPresenceGPIO()); 376 ON_CALL(*mockPresenceGPIO, read()).WillByDefault(Return(0)); 377 MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus()); 378 // Constructor should set initial presence, default read returns 0. 379 // If it is not present, I should not be trying to write to it. 380 EXPECT_CALL(mockPMBus, writeBinary(_, _, _)).Times(0); 381 psu.onOffConfig(data); 382 } 383 catch (...) 384 {} 385 386 // Test where PSU is present 387 try 388 { 389 // Assume GPIO presence, not inventory presence? 390 PowerSupply psu{bus, PSUInventoryPath, 5, 0x6a, PSUGPIOLineName}; 391 MockedGPIOInterface* mockPresenceGPIO = 392 static_cast<MockedGPIOInterface*>(psu.getPresenceGPIO()); 393 ON_CALL(*mockPresenceGPIO, read()).WillByDefault(Return(1)); 394 MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus()); 395 // TODO: expect setPresence call? 396 // updatePresence() private function reads gpio, called by analyze(). 397 psu.analyze(); 398 // TODO: ???should I check the filename? 399 EXPECT_CALL(mockPMBus, 400 writeBinary(_, ElementsAre(0x15), Type::HwmonDeviceDebug)) 401 .Times(1); 402 psu.onOffConfig(data); 403 } 404 catch (...) 405 {} 406 } 407 408 TEST_F(PowerSupplyTests, ClearFaults) 409 { 410 auto bus = sdbusplus::bus::new_default(); 411 PowerSupply psu{bus, PSUInventoryPath, 13, 0x68, PSUGPIOLineName}; 412 MockedGPIOInterface* mockPresenceGPIO = 413 static_cast<MockedGPIOInterface*>(psu.getPresenceGPIO()); 414 // GPIO read return 1 to indicate present. 415 ON_CALL(*mockPresenceGPIO, read()).WillByDefault(Return(1)); 416 MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus()); 417 // Presence change from missing to present will trigger in1_input read in 418 // an attempt to get CLEAR_FAULTS called. 419 EXPECT_CALL(mockPMBus, read(READ_VIN, _)).Times(1).WillOnce(Return(206000)); 420 // STATUS_WORD 0x0000 is powered on, no faults. 421 PMBusExpectations expectations; 422 setPMBusExpectations(mockPMBus, expectations); 423 psu.analyze(); 424 EXPECT_EQ(psu.isPresent(), true); 425 EXPECT_EQ(psu.isFaulted(), false); 426 EXPECT_EQ(psu.hasInputFault(), false); 427 EXPECT_EQ(psu.hasMFRFault(), false); 428 EXPECT_EQ(psu.hasVINUVFault(), false); 429 EXPECT_EQ(psu.hasCommFault(), false); 430 EXPECT_EQ(psu.hasVoutOVFault(), false); 431 432 // STATUS_WORD with fault bits galore! 433 expectations.statusWordValue = 0xFFFF; 434 // STATUS_INPUT with fault bits on. 435 expectations.statusInputValue = 0xFF; 436 // STATUS_MFR_SPEFIC with bits on. 437 expectations.statusMFRValue = 0xFF; 438 // STATUS_CML with bits on. 439 expectations.statusCMLValue = 0xFF; 440 // STATUS_VOUT with bits on. 441 expectations.statusVOUTValue = 0xFF; 442 setPMBusExpectations(mockPMBus, expectations); 443 psu.analyze(); 444 EXPECT_EQ(psu.isPresent(), true); 445 EXPECT_EQ(psu.isFaulted(), true); 446 EXPECT_EQ(psu.hasInputFault(), true); 447 EXPECT_EQ(psu.hasMFRFault(), true); 448 EXPECT_EQ(psu.hasVINUVFault(), true); 449 EXPECT_EQ(psu.hasCommFault(), true); 450 EXPECT_EQ(psu.hasVoutOVFault(), true); 451 EXPECT_CALL(mockPMBus, read("in1_input", _)) 452 .Times(1) 453 .WillOnce(Return(209000)); 454 psu.clearFaults(); 455 EXPECT_EQ(psu.isPresent(), true); 456 EXPECT_EQ(psu.isFaulted(), false); 457 EXPECT_EQ(psu.hasInputFault(), false); 458 EXPECT_EQ(psu.hasMFRFault(), false); 459 EXPECT_EQ(psu.hasVINUVFault(), false); 460 EXPECT_EQ(psu.hasCommFault(), false); 461 EXPECT_EQ(psu.hasVoutOVFault(), false); 462 463 // TODO: Faults clear on missing/present? 464 } 465 466 TEST_F(PowerSupplyTests, UpdateInventory) 467 { 468 auto bus = sdbusplus::bus::new_default(); 469 470 try 471 { 472 PowerSupply psu{bus, PSUInventoryPath, 3, 0x68, PSUGPIOLineName}; 473 MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus()); 474 // If it is not present, I should not be trying to read a string 475 EXPECT_CALL(mockPMBus, readString(_, _)).Times(0); 476 psu.updateInventory(); 477 } 478 catch (...) 479 { 480 ADD_FAILURE() << "Should not have caught exception."; 481 } 482 483 try 484 { 485 PowerSupply psu{bus, PSUInventoryPath, 13, 0x69, PSUGPIOLineName}; 486 MockedGPIOInterface* mockPresenceGPIO = 487 static_cast<MockedGPIOInterface*>(psu.getPresenceGPIO()); 488 // GPIO read return 1 to indicate present. 489 EXPECT_CALL(*mockPresenceGPIO, read()).Times(1).WillOnce(Return(1)); 490 psu.analyze(); 491 MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus()); 492 EXPECT_CALL(mockPMBus, readString(_, _)).WillRepeatedly(Return("")); 493 psu.updateInventory(); 494 495 #if IBM_VPD 496 EXPECT_CALL(mockPMBus, readString(_, _)) 497 .WillOnce(Return("CCIN")) 498 .WillOnce(Return("PN3456")) 499 .WillOnce(Return("FN3456")) 500 .WillOnce(Return("HEADER")) 501 .WillOnce(Return("SN3456")) 502 .WillOnce(Return("FW3456")); 503 #endif 504 psu.updateInventory(); 505 // TODO: D-Bus mocking to verify values stored on D-Bus (???) 506 } 507 catch (...) 508 { 509 ADD_FAILURE() << "Should not have caught exception."; 510 } 511 } 512 513 TEST_F(PowerSupplyTests, IsPresent) 514 { 515 auto bus = sdbusplus::bus::new_default(); 516 517 PowerSupply psu{bus, PSUInventoryPath, 3, 0x68, PSUGPIOLineName}; 518 MockedGPIOInterface* mockPresenceGPIO = 519 static_cast<MockedGPIOInterface*>(psu.getPresenceGPIO()); 520 EXPECT_EQ(psu.isPresent(), false); 521 522 // Change GPIO read to return 1 to indicate present. 523 EXPECT_CALL(*mockPresenceGPIO, read()).Times(1).WillOnce(Return(1)); 524 psu.analyze(); 525 EXPECT_EQ(psu.isPresent(), true); 526 } 527 528 TEST_F(PowerSupplyTests, IsFaulted) 529 { 530 auto bus = sdbusplus::bus::new_default(); 531 532 PowerSupply psu{bus, PSUInventoryPath, 11, 0x6f, PSUGPIOLineName}; 533 MockedGPIOInterface* mockPresenceGPIO = 534 static_cast<MockedGPIOInterface*>(psu.getPresenceGPIO()); 535 // Always return 1 to indicate present. 536 EXPECT_CALL(*mockPresenceGPIO, read()).WillRepeatedly(Return(1)); 537 psu.analyze(); 538 EXPECT_EQ(psu.isFaulted(), false); 539 MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus()); 540 PMBusExpectations expectations; 541 // STATUS_WORD with fault bits on. 542 expectations.statusWordValue = 0xFFFF; 543 // STATUS_INPUT with fault bits on. 544 expectations.statusInputValue = 0xFF; 545 // STATUS_MFR_SPECIFIC with faults bits on. 546 expectations.statusMFRValue = 0xFF; 547 // STATUS_CML with faults bits on. 548 expectations.statusCMLValue = 0xFF; 549 // STATUS_VOUT with fault bits on. 550 expectations.statusVOUTValue = 0xFF; 551 setPMBusExpectations(mockPMBus, expectations); 552 psu.analyze(); 553 EXPECT_EQ(psu.isFaulted(), true); 554 } 555 556 TEST_F(PowerSupplyTests, HasInputFault) 557 { 558 auto bus = sdbusplus::bus::new_default(); 559 560 PowerSupply psu{bus, PSUInventoryPath, 3, 0x68, PSUGPIOLineName}; 561 MockedGPIOInterface* mockPresenceGPIO = 562 static_cast<MockedGPIOInterface*>(psu.getPresenceGPIO()); 563 // Always return 1 to indicate present. 564 EXPECT_CALL(*mockPresenceGPIO, read()).WillRepeatedly(Return(1)); 565 psu.analyze(); 566 MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus()); 567 EXPECT_EQ(psu.hasInputFault(), false); 568 // STATUS_WORD 0x0000 is powered on, no faults. 569 PMBusExpectations expectations; 570 setPMBusExpectations(mockPMBus, expectations); 571 psu.analyze(); 572 EXPECT_EQ(psu.hasInputFault(), false); 573 // STATUS_WORD with input fault/warn on. 574 expectations.statusWordValue = (status_word::INPUT_FAULT_WARN); 575 // STATUS_INPUT with an input fault bit on. 576 expectations.statusInputValue = 0x80; 577 setPMBusExpectations(mockPMBus, expectations); 578 psu.analyze(); 579 EXPECT_EQ(psu.hasInputFault(), true); 580 // STATUS_WORD with no bits on. 581 expectations.statusWordValue = 0; 582 setPMBusExpectations(mockPMBus, expectations); 583 psu.analyze(); 584 EXPECT_EQ(psu.hasInputFault(), false); 585 } 586 587 TEST_F(PowerSupplyTests, HasMFRFault) 588 { 589 auto bus = sdbusplus::bus::new_default(); 590 591 PowerSupply psu{bus, PSUInventoryPath, 3, 0x68, PSUGPIOLineName}; 592 MockedGPIOInterface* mockPresenceGPIO = 593 static_cast<MockedGPIOInterface*>(psu.getPresenceGPIO()); 594 // Always return 1 to indicate present. 595 EXPECT_CALL(*mockPresenceGPIO, read()).WillRepeatedly(Return(1)); 596 psu.analyze(); 597 MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus()); 598 EXPECT_EQ(psu.hasMFRFault(), false); 599 // First return STATUS_WORD with no bits on. 600 // STATUS_WORD 0x0000 is powered on, no faults. 601 PMBusExpectations expectations; 602 setPMBusExpectations(mockPMBus, expectations); 603 psu.analyze(); 604 EXPECT_EQ(psu.hasMFRFault(), false); 605 // Next return STATUS_WORD with MFR fault bit on. 606 expectations.statusWordValue = (status_word::MFR_SPECIFIC_FAULT); 607 // STATUS_MFR_SPEFIC with bit(s) on. 608 expectations.statusMFRValue = 0xFF; 609 setPMBusExpectations(mockPMBus, expectations); 610 psu.analyze(); 611 EXPECT_EQ(psu.hasMFRFault(), true); 612 // Back to no bits on in STATUS_WORD 613 expectations.statusWordValue = 0; 614 setPMBusExpectations(mockPMBus, expectations); 615 psu.analyze(); 616 EXPECT_EQ(psu.hasMFRFault(), false); 617 } 618 619 TEST_F(PowerSupplyTests, HasVINUVFault) 620 { 621 auto bus = sdbusplus::bus::new_default(); 622 623 PowerSupply psu{bus, PSUInventoryPath, 3, 0x68, PSUGPIOLineName}; 624 MockedGPIOInterface* mockPresenceGPIO = 625 static_cast<MockedGPIOInterface*>(psu.getPresenceGPIO()); 626 // Always return 1 to indicate present. 627 EXPECT_CALL(*mockPresenceGPIO, read()).WillRepeatedly(Return(1)); 628 psu.analyze(); 629 MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus()); 630 EXPECT_EQ(psu.hasVINUVFault(), false); 631 // STATUS_WORD 0x0000 is powered on, no faults. 632 PMBusExpectations expectations; 633 setPMBusExpectations(mockPMBus, expectations); 634 psu.analyze(); 635 EXPECT_EQ(psu.hasVINUVFault(), false); 636 // Turn fault on. 637 expectations.statusWordValue = (status_word::VIN_UV_FAULT); 638 // Curious disagreement between PMBus Spec. Part II Figure 16 and 33. Go by 639 // Figure 16, and assume bits on in STATUS_INPUT. 640 expectations.statusInputValue = 0x18; 641 setPMBusExpectations(mockPMBus, expectations); 642 psu.analyze(); 643 EXPECT_EQ(psu.hasVINUVFault(), true); 644 // Back to no fault bits on in STATUS_WORD 645 expectations.statusWordValue = 0; 646 setPMBusExpectations(mockPMBus, expectations); 647 psu.analyze(); 648 EXPECT_EQ(psu.hasVINUVFault(), false); 649 } 650 651 TEST_F(PowerSupplyTests, HasVoutOVFault) 652 { 653 auto bus = sdbusplus::bus::new_default(); 654 655 PowerSupply psu{bus, PSUInventoryPath, 3, 0x69, PSUGPIOLineName}; 656 MockedGPIOInterface* mockPresenceGPIO = 657 static_cast<MockedGPIOInterface*>(psu.getPresenceGPIO()); 658 // Always return 1 to indicate present. 659 EXPECT_CALL(*mockPresenceGPIO, read()).WillRepeatedly(Return(1)); 660 psu.analyze(); 661 MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus()); 662 EXPECT_EQ(psu.hasVoutOVFault(), false); 663 // STATUS_WORD 0x0000 is powered on, no faults. 664 PMBusExpectations expectations; 665 setPMBusExpectations(mockPMBus, expectations); 666 psu.analyze(); 667 EXPECT_EQ(psu.hasVoutOVFault(), false); 668 // Turn fault on. 669 expectations.statusWordValue = (status_word::VOUT_OV_FAULT); 670 // STATUS_VOUT fault bit(s) 671 expectations.statusVOUTValue = 0x80; 672 setPMBusExpectations(mockPMBus, expectations); 673 psu.analyze(); 674 EXPECT_EQ(psu.hasVoutOVFault(), true); 675 // Back to no fault bits on in STATUS_WORD 676 expectations.statusWordValue = 0; 677 setPMBusExpectations(mockPMBus, expectations); 678 psu.analyze(); 679 EXPECT_EQ(psu.hasVoutOVFault(), false); 680 } 681