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