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 // Test where PSU is present 257 try 258 { 259 // Assume GPIO presence, not inventory presence? 260 PowerSupply psu{bus, PSUInventoryPath, 5, 0x6a, PSUGPIOLineName}; 261 MockedGPIOReader* mockPresenceGPIO = 262 static_cast<MockedGPIOReader*>(psu.getPresenceGPIO()); 263 ON_CALL(*mockPresenceGPIO, read()).WillByDefault(Return(1)); 264 MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus()); 265 // TODO: expect setPresence call? 266 // updatePresence() private function reads gpio, called by analyze(). 267 psu.analyze(); 268 // TODO: ???should I check the filename? 269 EXPECT_CALL(mockPMBus, 270 writeBinary(_, ElementsAre(0x15), Type::HwmonDeviceDebug)) 271 .Times(1); 272 psu.onOffConfig(data); 273 } 274 catch (...) 275 {} 276 } 277 278 TEST_F(PowerSupplyTests, ClearFaults) 279 { 280 auto bus = sdbusplus::bus::new_default(); 281 PowerSupply psu{bus, PSUInventoryPath, 13, 0x68, PSUGPIOLineName}; 282 MockedGPIOReader* mockPresenceGPIO = 283 static_cast<MockedGPIOReader*>(psu.getPresenceGPIO()); 284 // GPIO read return 1 to indicate present. 285 ON_CALL(*mockPresenceGPIO, read()).WillByDefault(Return(1)); 286 MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus()); 287 ON_CALL(mockPMBus, read(_, _)).WillByDefault(Return(0)); 288 psu.analyze(); 289 EXPECT_EQ(psu.isPresent(), true); 290 EXPECT_EQ(psu.isFaulted(), false); 291 EXPECT_EQ(psu.hasInputFault(), false); 292 EXPECT_EQ(psu.hasMFRFault(), false); 293 EXPECT_EQ(psu.hasVINUVFault(), false); 294 EXPECT_CALL(mockPMBus, read(_, _)) 295 .Times(2) 296 .WillOnce(Return(0xFFFF)) 297 .WillOnce(Return(1)); // mock return for read(STATUS_MFR... ) 298 psu.analyze(); 299 EXPECT_EQ(psu.isPresent(), true); 300 EXPECT_EQ(psu.isFaulted(), true); 301 EXPECT_EQ(psu.hasInputFault(), true); 302 EXPECT_EQ(psu.hasMFRFault(), true); 303 EXPECT_EQ(psu.hasVINUVFault(), true); 304 EXPECT_CALL(mockPMBus, read("in1_input", _)) 305 .Times(1) 306 .WillOnce(Return(209000)); 307 psu.clearFaults(); 308 EXPECT_EQ(psu.isPresent(), true); 309 EXPECT_EQ(psu.isFaulted(), false); 310 EXPECT_EQ(psu.hasInputFault(), false); 311 EXPECT_EQ(psu.hasMFRFault(), false); 312 EXPECT_EQ(psu.hasVINUVFault(), false); 313 314 // TODO: Faults clear on missing/present? 315 } 316 317 TEST_F(PowerSupplyTests, UpdateInventory) 318 { 319 auto bus = sdbusplus::bus::new_default(); 320 321 try 322 { 323 PowerSupply psu{bus, PSUInventoryPath, 3, 0x68, PSUGPIOLineName}; 324 MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus()); 325 // If it is not present, I should not be trying to read a string 326 EXPECT_CALL(mockPMBus, readString(_, _)).Times(0); 327 psu.updateInventory(); 328 } 329 catch (...) 330 { 331 ADD_FAILURE() << "Should not have caught exception."; 332 } 333 334 try 335 { 336 PowerSupply psu{bus, PSUInventoryPath, 13, 0x69, PSUGPIOLineName}; 337 MockedGPIOReader* mockPresenceGPIO = 338 static_cast<MockedGPIOReader*>(psu.getPresenceGPIO()); 339 // GPIO read return 1 to indicate present. 340 EXPECT_CALL(*mockPresenceGPIO, read()).Times(1).WillOnce(Return(1)); 341 psu.analyze(); 342 MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus()); 343 EXPECT_CALL(mockPMBus, readString(_, _)).WillRepeatedly(Return("")); 344 psu.updateInventory(); 345 346 #if IBM_VPD 347 EXPECT_CALL(mockPMBus, readString(_, _)) 348 .WillOnce(Return("CCIN")) 349 .WillOnce(Return("PN3456")) 350 .WillOnce(Return("FN3456")) 351 .WillOnce(Return("HEADER")) 352 .WillOnce(Return("SN3456")) 353 .WillOnce(Return("FW3456")); 354 #endif 355 psu.updateInventory(); 356 // TODO: D-Bus mocking to verify values stored on D-Bus (???) 357 } 358 catch (...) 359 { 360 ADD_FAILURE() << "Should not have caught exception."; 361 } 362 } 363 364 TEST_F(PowerSupplyTests, IsPresent) 365 { 366 auto bus = sdbusplus::bus::new_default(); 367 368 PowerSupply psu{bus, PSUInventoryPath, 3, 0x68, PSUGPIOLineName}; 369 MockedGPIOReader* mockPresenceGPIO = 370 static_cast<MockedGPIOReader*>(psu.getPresenceGPIO()); 371 EXPECT_EQ(psu.isPresent(), false); 372 373 // Change GPIO read to return 1 to indicate present. 374 EXPECT_CALL(*mockPresenceGPIO, read()).Times(1).WillOnce(Return(1)); 375 psu.analyze(); 376 EXPECT_EQ(psu.isPresent(), true); 377 } 378 379 TEST_F(PowerSupplyTests, IsFaulted) 380 { 381 auto bus = sdbusplus::bus::new_default(); 382 383 PowerSupply psu{bus, PSUInventoryPath, 11, 0x6f, PSUGPIOLineName}; 384 MockedGPIOReader* mockPresenceGPIO = 385 static_cast<MockedGPIOReader*>(psu.getPresenceGPIO()); 386 // Always return 1 to indicate present. 387 EXPECT_CALL(*mockPresenceGPIO, read()).WillRepeatedly(Return(1)); 388 psu.analyze(); 389 EXPECT_EQ(psu.isFaulted(), false); 390 MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus()); 391 EXPECT_CALL(mockPMBus, read(_, _)) 392 .Times(2) 393 .WillOnce(Return(0xFFFF)) 394 .WillOnce(Return(1)); // mock return for read(STATUS_MFR... ) 395 psu.analyze(); 396 EXPECT_EQ(psu.isFaulted(), true); 397 } 398 399 TEST_F(PowerSupplyTests, HasInputFault) 400 { 401 auto bus = sdbusplus::bus::new_default(); 402 403 PowerSupply psu{bus, PSUInventoryPath, 3, 0x68, PSUGPIOLineName}; 404 MockedGPIOReader* mockPresenceGPIO = 405 static_cast<MockedGPIOReader*>(psu.getPresenceGPIO()); 406 // Always return 1 to indicate present. 407 EXPECT_CALL(*mockPresenceGPIO, read()).WillRepeatedly(Return(1)); 408 psu.analyze(); 409 MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus()); 410 EXPECT_EQ(psu.hasInputFault(), false); 411 EXPECT_CALL(mockPMBus, read(_, _)).Times(1).WillOnce(Return(0x0000)); 412 psu.analyze(); 413 EXPECT_EQ(psu.hasInputFault(), false); 414 EXPECT_CALL(mockPMBus, read(_, _)) 415 .Times(2) 416 .WillOnce(Return(status_word::INPUT_FAULT_WARN)) 417 .WillOnce(Return(0)); 418 psu.analyze(); 419 EXPECT_EQ(psu.hasInputFault(), true); 420 EXPECT_CALL(mockPMBus, read(_, _)).Times(1).WillOnce(Return(0x0000)); 421 psu.analyze(); 422 EXPECT_EQ(psu.hasInputFault(), false); 423 } 424 425 TEST_F(PowerSupplyTests, HasMFRFault) 426 { 427 auto bus = sdbusplus::bus::new_default(); 428 429 PowerSupply psu{bus, PSUInventoryPath, 3, 0x68, PSUGPIOLineName}; 430 MockedGPIOReader* mockPresenceGPIO = 431 static_cast<MockedGPIOReader*>(psu.getPresenceGPIO()); 432 // Always return 1 to indicate present. 433 EXPECT_CALL(*mockPresenceGPIO, read()).WillRepeatedly(Return(1)); 434 psu.analyze(); 435 MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus()); 436 EXPECT_EQ(psu.hasMFRFault(), false); 437 EXPECT_CALL(mockPMBus, read(_, _)).Times(1).WillOnce(Return(0x0000)); 438 psu.analyze(); 439 EXPECT_EQ(psu.hasMFRFault(), false); 440 EXPECT_CALL(mockPMBus, read(_, _)) 441 .Times(2) 442 .WillOnce(Return(status_word::MFR_SPECIFIC_FAULT)) 443 .WillOnce(Return(1)); // mock return for read(STATUS_MFR... ) 444 psu.analyze(); 445 EXPECT_EQ(psu.hasMFRFault(), true); 446 EXPECT_CALL(mockPMBus, read(_, _)).Times(1).WillOnce(Return(0x0000)); 447 psu.analyze(); 448 EXPECT_EQ(psu.hasMFRFault(), false); 449 } 450 451 TEST_F(PowerSupplyTests, HasVINUVFault) 452 { 453 auto bus = sdbusplus::bus::new_default(); 454 455 PowerSupply psu{bus, PSUInventoryPath, 3, 0x68, PSUGPIOLineName}; 456 MockedGPIOReader* mockPresenceGPIO = 457 static_cast<MockedGPIOReader*>(psu.getPresenceGPIO()); 458 // Always return 1 to indicate present. 459 EXPECT_CALL(*mockPresenceGPIO, read()).WillRepeatedly(Return(1)); 460 psu.analyze(); 461 MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus()); 462 EXPECT_EQ(psu.hasVINUVFault(), false); 463 EXPECT_CALL(mockPMBus, read(_, _)).Times(1).WillOnce(Return(0x0000)); 464 psu.analyze(); 465 EXPECT_EQ(psu.hasVINUVFault(), false); 466 EXPECT_CALL(mockPMBus, read(_, _)) 467 .Times(2) 468 .WillOnce(Return(status_word::VIN_UV_FAULT)) 469 .WillOnce(Return(0)); 470 psu.analyze(); 471 EXPECT_EQ(psu.hasVINUVFault(), true); 472 EXPECT_CALL(mockPMBus, read(_, _)).Times(1).WillOnce(Return(0x0000)); 473 psu.analyze(); 474 EXPECT_EQ(psu.hasVINUVFault(), false); 475 } 476