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, "0068"); 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, "0068"); 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, "0069"}; 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, "006a"}; 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(1) 120 .WillOnce(Return(status_word::INPUT_FAULT_WARN)); 121 psu2.analyze(); 122 EXPECT_EQ(psu2.isPresent(), true); 123 EXPECT_EQ(psu2.isFaulted(), true); 124 EXPECT_EQ(psu2.hasInputFault(), true); 125 EXPECT_EQ(psu2.hasMFRFault(), false); 126 EXPECT_EQ(psu2.hasVINUVFault(), false); 127 128 // STATUS_WORD INPUT/UV fault. 129 // First need it to return good status, then the fault 130 EXPECT_CALL(mockPMBus, read(_, _)) 131 .WillOnce(Return(0x0000)) 132 .WillOnce(Return(status_word::VIN_UV_FAULT)); 133 psu2.analyze(); 134 psu2.analyze(); 135 EXPECT_EQ(psu2.isPresent(), true); 136 EXPECT_EQ(psu2.isFaulted(), true); 137 EXPECT_EQ(psu2.hasInputFault(), false); 138 EXPECT_EQ(psu2.hasMFRFault(), false); 139 EXPECT_EQ(psu2.hasVINUVFault(), true); 140 141 // STATUS_WORD MFR fault. 142 EXPECT_CALL(mockPMBus, read(_, _)) 143 .WillOnce(Return(0x0000)) 144 .WillOnce(Return(status_word::MFR_SPECIFIC_FAULT)); 145 psu2.analyze(); 146 psu2.analyze(); 147 EXPECT_EQ(psu2.isPresent(), true); 148 EXPECT_EQ(psu2.isFaulted(), true); 149 EXPECT_EQ(psu2.hasInputFault(), false); 150 EXPECT_EQ(psu2.hasMFRFault(), true); 151 EXPECT_EQ(psu2.hasVINUVFault(), false); 152 153 // Ignore Temperature fault. 154 EXPECT_CALL(mockPMBus, read(_, _)) 155 .WillOnce(Return(0x0000)) 156 .WillOnce(Return(status_word::TEMPERATURE_FAULT_WARN)); 157 psu2.analyze(); 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 // Ignore fan fault 166 EXPECT_CALL(mockPMBus, read(_, _)) 167 .WillOnce(Return(0x0000)) 168 .WillOnce(Return(status_word::FAN_FAULT)); 169 psu2.analyze(); 170 psu2.analyze(); 171 EXPECT_EQ(psu2.isPresent(), true); 172 EXPECT_EQ(psu2.isFaulted(), false); 173 EXPECT_EQ(psu2.hasInputFault(), false); 174 EXPECT_EQ(psu2.hasMFRFault(), false); 175 EXPECT_EQ(psu2.hasVINUVFault(), false); 176 177 // TODO: ReadFailure 178 } 179 180 TEST_F(PowerSupplyTests, OnOffConfig) 181 { 182 auto bus = sdbusplus::bus::new_default(); 183 uint8_t data = 0x15; 184 185 // Test where PSU is NOT present 186 try 187 { 188 EXPECT_CALL(mockedUtil, getPresence(_, StrEq(PSUInventoryPath))) 189 .Times(1) 190 .WillOnce(Return(false)); 191 PowerSupply psu{bus, PSUInventoryPath, 4, "0069"}; 192 MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus()); 193 // If it is not present, I should not be trying to write to it. 194 EXPECT_CALL(mockPMBus, writeBinary(_, _, _)).Times(0); 195 psu.onOffConfig(data); 196 } 197 catch (...) 198 { 199 } 200 201 // Test where PSU is present 202 try 203 { 204 EXPECT_CALL(mockedUtil, getPresence(_, StrEq(PSUInventoryPath))) 205 .Times(1) 206 .WillOnce(Return(true)); // present 207 PowerSupply psu{bus, PSUInventoryPath, 5, "006a"}; 208 MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus()); 209 // TODO: ???should I check the filename? 210 EXPECT_CALL(mockPMBus, 211 writeBinary(_, ElementsAre(0x15), Type::HwmonDeviceDebug)) 212 .Times(1); 213 psu.onOffConfig(data); 214 } 215 catch (...) 216 { 217 } 218 } 219 220 TEST_F(PowerSupplyTests, ClearFaults) 221 { 222 auto bus = sdbusplus::bus::new_default(); 223 EXPECT_CALL(mockedUtil, getPresence(_, StrEq(PSUInventoryPath))) 224 .Times(1) 225 .WillOnce(Return(true)); // present 226 PowerSupply psu{bus, PSUInventoryPath, 13, "0068"}; 227 EXPECT_EQ(psu.isPresent(), true); 228 EXPECT_EQ(psu.isFaulted(), false); 229 EXPECT_EQ(psu.hasInputFault(), false); 230 EXPECT_EQ(psu.hasMFRFault(), false); 231 EXPECT_EQ(psu.hasVINUVFault(), false); 232 MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus()); 233 EXPECT_CALL(mockPMBus, read(_, _)).Times(1).WillOnce(Return(0xFFFF)); 234 psu.analyze(); 235 EXPECT_EQ(psu.isPresent(), true); 236 EXPECT_EQ(psu.isFaulted(), true); 237 EXPECT_EQ(psu.hasInputFault(), true); 238 EXPECT_EQ(psu.hasMFRFault(), true); 239 EXPECT_EQ(psu.hasVINUVFault(), true); 240 EXPECT_CALL(mockPMBus, read("in1_input", _)) 241 .Times(1) 242 .WillOnce(Return(209000)); 243 psu.clearFaults(); 244 EXPECT_EQ(psu.isPresent(), true); 245 EXPECT_EQ(psu.isFaulted(), false); 246 EXPECT_EQ(psu.hasInputFault(), false); 247 EXPECT_EQ(psu.hasMFRFault(), false); 248 EXPECT_EQ(psu.hasVINUVFault(), false); 249 } 250 251 TEST_F(PowerSupplyTests, UpdateInventory) 252 { 253 auto bus = sdbusplus::bus::new_default(); 254 255 try 256 { 257 EXPECT_CALL(mockedUtil, getPresence(_, StrEq(PSUInventoryPath))) 258 .Times(1) 259 .WillOnce(Return(false)); // missing 260 PowerSupply psu{bus, PSUInventoryPath, 3, "0068"}; 261 MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus()); 262 // If it is not present, I should not be trying to read a string 263 EXPECT_CALL(mockPMBus, readString(_, _)).Times(0); 264 psu.updateInventory(); 265 } 266 catch (...) 267 { 268 ADD_FAILURE() << "Should not have caught exception."; 269 } 270 271 try 272 { 273 EXPECT_CALL(mockedUtil, getPresence(_, StrEq(PSUInventoryPath))) 274 .Times(1) 275 .WillOnce(Return(true)); // present 276 PowerSupply psu{bus, PSUInventoryPath, 13, "0069"}; 277 MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus()); 278 EXPECT_CALL(mockPMBus, readString(_, _)).WillRepeatedly(Return("")); 279 psu.updateInventory(); 280 281 EXPECT_CALL(mockPMBus, readString(_, _)) 282 .WillOnce(Return("CCIN")) 283 .WillOnce(Return("PN3456")) 284 .WillOnce(Return("FN3456")) 285 .WillOnce(Return("HEADER")) 286 .WillOnce(Return("SN3456")) 287 .WillOnce(Return("FW3456")); 288 psu.updateInventory(); 289 // TODO: D-Bus mocking to verify values stored on D-Bus (???) 290 } 291 catch (...) 292 { 293 ADD_FAILURE() << "Should not have caught exception."; 294 } 295 } 296 297 TEST_F(PowerSupplyTests, IsPresent) 298 { 299 auto bus = sdbusplus::bus::new_default(); 300 EXPECT_CALL(mockedUtil, getPresence(_, StrEq(PSUInventoryPath))).Times(1); 301 PowerSupply psu{bus, PSUInventoryPath, 3, "0068"}; 302 EXPECT_EQ(psu.isPresent(), false); 303 304 EXPECT_CALL(mockedUtil, getPresence(_, _)) 305 .WillOnce(Return(true)); // present 306 PowerSupply psu2{bus, PSUInventoryPath, 10, "006b"}; 307 EXPECT_EQ(psu2.isPresent(), true); 308 } 309 310 TEST_F(PowerSupplyTests, IsFaulted) 311 { 312 auto bus = sdbusplus::bus::new_default(); 313 EXPECT_CALL(mockedUtil, getPresence(_, _)) 314 .WillOnce(Return(true)); // present 315 PowerSupply psu{bus, PSUInventoryPath, 11, "006f"}; 316 EXPECT_EQ(psu.isFaulted(), false); 317 MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus()); 318 EXPECT_CALL(mockPMBus, read(_, _)).Times(1).WillOnce(Return(0xFFFF)); 319 psu.analyze(); 320 EXPECT_EQ(psu.isFaulted(), true); 321 } 322 323 TEST_F(PowerSupplyTests, HasInputFault) 324 { 325 auto bus = sdbusplus::bus::new_default(); 326 EXPECT_CALL(mockedUtil, getPresence(_, _)) 327 .WillOnce(Return(true)); // present 328 PowerSupply psu{bus, PSUInventoryPath, 3, "0068"}; 329 MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus()); 330 EXPECT_EQ(psu.hasInputFault(), false); 331 EXPECT_CALL(mockPMBus, read(_, _)).Times(1).WillOnce(Return(0x0000)); 332 psu.analyze(); 333 EXPECT_EQ(psu.hasInputFault(), false); 334 EXPECT_CALL(mockPMBus, read(_, _)) 335 .Times(1) 336 .WillOnce(Return(status_word::INPUT_FAULT_WARN)); 337 psu.analyze(); 338 EXPECT_EQ(psu.hasInputFault(), true); 339 EXPECT_CALL(mockPMBus, read(_, _)).Times(1).WillOnce(Return(0x0000)); 340 psu.analyze(); 341 EXPECT_EQ(psu.hasInputFault(), false); 342 } 343 344 TEST_F(PowerSupplyTests, HasMFRFault) 345 { 346 auto bus = sdbusplus::bus::new_default(); 347 EXPECT_CALL(mockedUtil, getPresence(_, _)) 348 .WillOnce(Return(true)); // present 349 PowerSupply psu{bus, PSUInventoryPath, 3, "0068"}; 350 MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus()); 351 EXPECT_EQ(psu.hasMFRFault(), false); 352 EXPECT_CALL(mockPMBus, read(_, _)).Times(1).WillOnce(Return(0x0000)); 353 psu.analyze(); 354 EXPECT_EQ(psu.hasMFRFault(), false); 355 EXPECT_CALL(mockPMBus, read(_, _)) 356 .Times(1) 357 .WillOnce(Return(status_word::MFR_SPECIFIC_FAULT)); 358 psu.analyze(); 359 EXPECT_EQ(psu.hasMFRFault(), true); 360 EXPECT_CALL(mockPMBus, read(_, _)).Times(1).WillOnce(Return(0x0000)); 361 psu.analyze(); 362 EXPECT_EQ(psu.hasMFRFault(), false); 363 } 364 365 TEST_F(PowerSupplyTests, HasVINUVFault) 366 { 367 auto bus = sdbusplus::bus::new_default(); 368 EXPECT_CALL(mockedUtil, getPresence(_, _)) 369 .WillOnce(Return(true)); // present 370 PowerSupply psu{bus, PSUInventoryPath, 3, "0068"}; 371 MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus()); 372 EXPECT_EQ(psu.hasVINUVFault(), false); 373 EXPECT_CALL(mockPMBus, read(_, _)).Times(1).WillOnce(Return(0x0000)); 374 psu.analyze(); 375 EXPECT_EQ(psu.hasVINUVFault(), false); 376 EXPECT_CALL(mockPMBus, read(_, _)) 377 .Times(1) 378 .WillOnce(Return(status_word::VIN_UV_FAULT)); 379 psu.analyze(); 380 EXPECT_EQ(psu.hasVINUVFault(), true); 381 EXPECT_CALL(mockPMBus, read(_, _)).Times(1).WillOnce(Return(0x0000)); 382 psu.analyze(); 383 EXPECT_EQ(psu.hasVINUVFault(), false); 384 } 385