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