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     MockedGPIOInterface* mockPresenceGPIO =
134         static_cast<MockedGPIOInterface*>(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     EXPECT_EQ(psu.hasCommFault(), false);
145 
146     PowerSupply psu2{bus, PSUInventoryPath, 5, 0x6a, PSUGPIOLineName};
147     // In order to get the various faults tested, the power supply needs to
148     // be present in order to read from the PMBus device(s).
149     MockedGPIOInterface* mockPresenceGPIO2 =
150         static_cast<MockedGPIOInterface*>(psu2.getPresenceGPIO());
151     ON_CALL(*mockPresenceGPIO2, read()).WillByDefault(Return(1));
152 
153     EXPECT_EQ(psu2.isPresent(), false);
154 
155     MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu2.getPMBus());
156     // Presence change from missing to present will trigger in1_input read in
157     // an attempt to get CLEAR_FAULTS called.
158     EXPECT_CALL(mockPMBus, read(READ_VIN, _)).Times(1).WillOnce(Return(206000));
159     // STATUS_WORD 0x0000 is powered on, no faults.
160     EXPECT_CALL(mockPMBus, read(STATUS_WORD, _))
161         .Times(1)
162         .WillOnce(Return(0x0000));
163     psu2.analyze();
164     EXPECT_EQ(psu2.isPresent(), true);
165     EXPECT_EQ(psu2.isFaulted(), false);
166     EXPECT_EQ(psu2.hasInputFault(), false);
167     EXPECT_EQ(psu2.hasMFRFault(), false);
168     EXPECT_EQ(psu2.hasVINUVFault(), false);
169     EXPECT_EQ(psu2.hasCommFault(), false);
170 
171     // STATUS_WORD input fault/warn
172     EXPECT_CALL(mockPMBus, read(STATUS_WORD, _))
173         .Times(1)
174         .WillOnce(Return(status_word::INPUT_FAULT_WARN));
175     // Due to the fault bit on in STATUS_WORD, there will also be a read of
176     // STATUS_INPUT, STATUS_MFR, and STATUS_CML, so there should be 4 reads
177     // total expected.
178     // STATUS_INPUT fault bits ... on.
179     EXPECT_CALL(mockPMBus, read(STATUS_INPUT, _))
180         .Times(1)
181         .WillOnce(Return(0x38));
182     // STATUS_MFR don't care
183     EXPECT_CALL(mockPMBus, read(STATUS_MFR, _)).Times(1).WillOnce(Return(0));
184     // STATUS_CML don't care
185     EXPECT_CALL(mockPMBus, read(STATUS_CML, _)).Times(1).WillOnce(Return(0));
186     psu2.analyze();
187     EXPECT_EQ(psu2.isPresent(), true);
188     EXPECT_EQ(psu2.isFaulted(), true);
189     EXPECT_EQ(psu2.hasInputFault(), true);
190     EXPECT_EQ(psu2.hasMFRFault(), false);
191     EXPECT_EQ(psu2.hasVINUVFault(), false);
192     EXPECT_EQ(psu2.hasCommFault(), false);
193 
194     // STATUS_WORD INPUT/UV fault.
195     // First need it to return good status, then the fault
196     EXPECT_CALL(mockPMBus, read(STATUS_WORD, _))
197         .Times(2)
198         .WillOnce(Return(0x0000))
199         .WillOnce(Return(
200             (status_word::INPUT_FAULT_WARN | status_word::VIN_UV_FAULT)));
201     // STATUS_INPUT fault bits ... on.
202     EXPECT_CALL(mockPMBus, read(STATUS_INPUT, _))
203         .Times(1)
204         .WillOnce(Return(0x38));
205     // STATUS_MFR don't care
206     EXPECT_CALL(mockPMBus, read(STATUS_MFR, _)).Times(1).WillOnce(Return(0));
207     // STATUS_CML don't care
208     EXPECT_CALL(mockPMBus, read(STATUS_CML, _)).Times(1).WillOnce(Return(0));
209     psu2.analyze();
210     psu2.analyze();
211     EXPECT_EQ(psu2.isPresent(), true);
212     EXPECT_EQ(psu2.isFaulted(), true);
213     EXPECT_EQ(psu2.hasInputFault(), true);
214     EXPECT_EQ(psu2.hasMFRFault(), false);
215     EXPECT_EQ(psu2.hasVINUVFault(), true);
216     EXPECT_EQ(psu2.hasCommFault(), false);
217 
218     // STATUS_WORD MFR fault.
219     // First need it to return good status, then the fault
220     EXPECT_CALL(mockPMBus, read(STATUS_WORD, _))
221         .Times(2)
222         .WillOnce(Return(0x0000))
223         .WillOnce(Return(status_word::MFR_SPECIFIC_FAULT));
224     // STATUS_INPUT fault bits ... don't care.
225     EXPECT_CALL(mockPMBus, read(STATUS_INPUT, _))
226         .Times(1)
227         .WillOnce(Return(0x00));
228     // STATUS_MFR bits on.
229     EXPECT_CALL(mockPMBus, read(STATUS_MFR, _)).Times(1).WillOnce(Return(0xFF));
230     // STATUS_CML don't care
231     EXPECT_CALL(mockPMBus, read(STATUS_CML, _)).Times(1).WillOnce(Return(0));
232     psu2.analyze();
233     psu2.analyze();
234     EXPECT_EQ(psu2.isPresent(), true);
235     EXPECT_EQ(psu2.isFaulted(), true);
236     EXPECT_EQ(psu2.hasInputFault(), false);
237     EXPECT_EQ(psu2.hasMFRFault(), true);
238     EXPECT_EQ(psu2.hasVINUVFault(), false);
239     EXPECT_EQ(psu2.hasCommFault(), false);
240 
241     // Ignore Temperature fault.
242     // First STATUS_WORD with no bits set, then with temperature fault.
243     EXPECT_CALL(mockPMBus, read(STATUS_WORD, _))
244         .Times(2)
245         .WillOnce(Return(0x0000))
246         .WillOnce(Return(status_word::TEMPERATURE_FAULT_WARN));
247     // If the STATUS_WORD has bits on, STATUS_MFR_SPECIFIC, STATUS_INPUT, and
248     // STATUS_CML will also be read.
249     // STATUS_INPUT fault bits ... don't care.
250     EXPECT_CALL(mockPMBus, read(STATUS_INPUT, _))
251         .Times(1)
252         .WillOnce(Return(0x00));
253     // STATUS_MFR don't care
254     EXPECT_CALL(mockPMBus, read(STATUS_MFR, _)).Times(1).WillOnce(Return(0));
255     // STATUS_CML don't care
256     EXPECT_CALL(mockPMBus, read(STATUS_CML, _)).Times(1).WillOnce(Return(0));
257     psu2.analyze();
258     psu2.analyze();
259     EXPECT_EQ(psu2.isPresent(), true);
260     EXPECT_EQ(psu2.isFaulted(), false);
261     EXPECT_EQ(psu2.hasInputFault(), false);
262     EXPECT_EQ(psu2.hasMFRFault(), false);
263     EXPECT_EQ(psu2.hasVINUVFault(), false);
264     EXPECT_EQ(psu2.hasCommFault(), false);
265 
266     // CML fault
267     // First STATUS_WORD wit no bits set, then with CML fault.
268     // STATUS_WORD with CML fault bit on.
269     EXPECT_CALL(mockPMBus, read(STATUS_WORD, _))
270         .Times(2)
271         .WillOnce(Return(0x0000))
272         .WillOnce(Return(status_word::CML_FAULT));
273     psu2.analyze();
274     // If the STATUS_WORD has bits on, STATUS_MFR_SPECIFIC, STATUS_INPUT, and
275     // STATUS_CML will also be read.
276     EXPECT_CALL(mockPMBus, read(STATUS_INPUT, _))
277         .Times(1)
278         .WillOnce(Return(0x00));
279     EXPECT_CALL(mockPMBus, read(STATUS_MFR, _)).Times(1).WillOnce(Return(0x00));
280     // Turn on STATUS_CML fault bit(s)
281     EXPECT_CALL(mockPMBus, read(STATUS_CML, _)).Times(1).WillOnce(Return(0xFF));
282     psu2.analyze();
283     EXPECT_EQ(psu2.isPresent(), true);
284     EXPECT_EQ(psu2.isFaulted(), true);
285     EXPECT_EQ(psu2.hasInputFault(), false);
286     EXPECT_EQ(psu2.hasMFRFault(), false);
287     EXPECT_EQ(psu2.hasVINUVFault(), false);
288     EXPECT_EQ(psu2.hasCommFault(), true);
289 
290     // Ignore fan fault
291     // First STATUS_WORD with no bits set, then with fan fault.
292     EXPECT_CALL(mockPMBus, read(STATUS_WORD, _))
293         .Times(2)
294         .WillOnce(Return(0x0000))
295         .WillOnce(Return(status_word::FAN_FAULT));
296     // If the STATUS_WORD has bits on, STATUS_MFR_SPECIFIC, STATUS_INPUT, and
297     // STATUS_CML will also be read.
298     // Don't care if bits set or not.
299     EXPECT_CALL(mockPMBus, read(STATUS_INPUT, _))
300         .Times(1)
301         .WillOnce(Return(0x00));
302     EXPECT_CALL(mockPMBus, read(STATUS_MFR, _)).Times(1).WillOnce(Return(0));
303     EXPECT_CALL(mockPMBus, read(STATUS_CML, _)).Times(1).WillOnce(Return(0));
304     psu2.analyze();
305     psu2.analyze();
306     EXPECT_EQ(psu2.isPresent(), true);
307     EXPECT_EQ(psu2.isFaulted(), false);
308     EXPECT_EQ(psu2.hasInputFault(), false);
309     EXPECT_EQ(psu2.hasMFRFault(), false);
310     EXPECT_EQ(psu2.hasVINUVFault(), false);
311     EXPECT_EQ(psu2.hasCommFault(), false);
312 
313     // TODO: ReadFailure
314 }
315 
316 TEST_F(PowerSupplyTests, OnOffConfig)
317 {
318     auto bus = sdbusplus::bus::new_default();
319     uint8_t data = 0x15;
320 
321     // Test where PSU is NOT present
322     try
323     {
324         // Assume GPIO presence, not inventory presence?
325         PowerSupply psu{bus, PSUInventoryPath, 4, 0x69, PSUGPIOLineName};
326 
327         MockedGPIOInterface* mockPresenceGPIO =
328             static_cast<MockedGPIOInterface*>(psu.getPresenceGPIO());
329         ON_CALL(*mockPresenceGPIO, read()).WillByDefault(Return(0));
330         MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus());
331         // Constructor should set initial presence, default read returns 0.
332         // If it is not present, I should not be trying to write to it.
333         EXPECT_CALL(mockPMBus, writeBinary(_, _, _)).Times(0);
334         psu.onOffConfig(data);
335     }
336     catch (...)
337     {}
338 
339     // Test where PSU is present
340     try
341     {
342         // Assume GPIO presence, not inventory presence?
343         PowerSupply psu{bus, PSUInventoryPath, 5, 0x6a, PSUGPIOLineName};
344         MockedGPIOInterface* mockPresenceGPIO =
345             static_cast<MockedGPIOInterface*>(psu.getPresenceGPIO());
346         ON_CALL(*mockPresenceGPIO, read()).WillByDefault(Return(1));
347         MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus());
348         // TODO: expect setPresence call?
349         // updatePresence() private function reads gpio, called by analyze().
350         psu.analyze();
351         // TODO: ???should I check the filename?
352         EXPECT_CALL(mockPMBus,
353                     writeBinary(_, ElementsAre(0x15), Type::HwmonDeviceDebug))
354             .Times(1);
355         psu.onOffConfig(data);
356     }
357     catch (...)
358     {}
359 }
360 
361 TEST_F(PowerSupplyTests, ClearFaults)
362 {
363     auto bus = sdbusplus::bus::new_default();
364     PowerSupply psu{bus, PSUInventoryPath, 13, 0x68, PSUGPIOLineName};
365     MockedGPIOInterface* mockPresenceGPIO =
366         static_cast<MockedGPIOInterface*>(psu.getPresenceGPIO());
367     // GPIO read return 1 to indicate present.
368     ON_CALL(*mockPresenceGPIO, read()).WillByDefault(Return(1));
369     MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus());
370     ON_CALL(mockPMBus, read(STATUS_WORD, _)).WillByDefault(Return(0));
371     psu.analyze();
372     EXPECT_EQ(psu.isPresent(), true);
373     EXPECT_EQ(psu.isFaulted(), false);
374     EXPECT_EQ(psu.hasInputFault(), false);
375     EXPECT_EQ(psu.hasMFRFault(), false);
376     EXPECT_EQ(psu.hasVINUVFault(), false);
377     EXPECT_EQ(psu.hasCommFault(), false);
378     // STATUS_WORD with fault bits galore!
379     EXPECT_CALL(mockPMBus, read(STATUS_WORD, _))
380         .Times(1)
381         .WillOnce(Return(0xFFFF));
382     // If STATUS_WORD has any fault bits on, STATUS_MFR_SPECIFIC, STATUS_INPUT
383     // and STATUS_CML will be read.
384     // STATUS_INPUT with fault bits on.
385     EXPECT_CALL(mockPMBus, read(STATUS_INPUT, _))
386         .Times(1)
387         .WillOnce(Return(0xFF));
388     // STATUS_MFR_SPEFIC with bits on.
389     EXPECT_CALL(mockPMBus, read(STATUS_MFR, _)).Times(1).WillOnce(Return(0xFF));
390     // STATUS_CML with bits on.
391     EXPECT_CALL(mockPMBus, read(STATUS_CML, _)).Times(1).WillOnce(Return(0xFF));
392     psu.analyze();
393     EXPECT_EQ(psu.isPresent(), true);
394     EXPECT_EQ(psu.isFaulted(), true);
395     EXPECT_EQ(psu.hasInputFault(), true);
396     EXPECT_EQ(psu.hasMFRFault(), true);
397     EXPECT_EQ(psu.hasVINUVFault(), true);
398     EXPECT_EQ(psu.hasCommFault(), true);
399     EXPECT_CALL(mockPMBus, read("in1_input", _))
400         .Times(1)
401         .WillOnce(Return(209000));
402     psu.clearFaults();
403     EXPECT_EQ(psu.isPresent(), true);
404     EXPECT_EQ(psu.isFaulted(), false);
405     EXPECT_EQ(psu.hasInputFault(), false);
406     EXPECT_EQ(psu.hasMFRFault(), false);
407     EXPECT_EQ(psu.hasVINUVFault(), false);
408     EXPECT_EQ(psu.hasCommFault(), false);
409 
410     // TODO: Faults clear on missing/present?
411 }
412 
413 TEST_F(PowerSupplyTests, UpdateInventory)
414 {
415     auto bus = sdbusplus::bus::new_default();
416 
417     try
418     {
419         PowerSupply psu{bus, PSUInventoryPath, 3, 0x68, PSUGPIOLineName};
420         MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus());
421         // If it is not present, I should not be trying to read a string
422         EXPECT_CALL(mockPMBus, readString(_, _)).Times(0);
423         psu.updateInventory();
424     }
425     catch (...)
426     {
427         ADD_FAILURE() << "Should not have caught exception.";
428     }
429 
430     try
431     {
432         PowerSupply psu{bus, PSUInventoryPath, 13, 0x69, PSUGPIOLineName};
433         MockedGPIOInterface* mockPresenceGPIO =
434             static_cast<MockedGPIOInterface*>(psu.getPresenceGPIO());
435         // GPIO read return 1 to indicate present.
436         EXPECT_CALL(*mockPresenceGPIO, read()).Times(1).WillOnce(Return(1));
437         psu.analyze();
438         MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus());
439         EXPECT_CALL(mockPMBus, readString(_, _)).WillRepeatedly(Return(""));
440         psu.updateInventory();
441 
442 #if IBM_VPD
443         EXPECT_CALL(mockPMBus, readString(_, _))
444             .WillOnce(Return("CCIN"))
445             .WillOnce(Return("PN3456"))
446             .WillOnce(Return("FN3456"))
447             .WillOnce(Return("HEADER"))
448             .WillOnce(Return("SN3456"))
449             .WillOnce(Return("FW3456"));
450 #endif
451         psu.updateInventory();
452         // TODO: D-Bus mocking to verify values stored on D-Bus (???)
453     }
454     catch (...)
455     {
456         ADD_FAILURE() << "Should not have caught exception.";
457     }
458 }
459 
460 TEST_F(PowerSupplyTests, IsPresent)
461 {
462     auto bus = sdbusplus::bus::new_default();
463 
464     PowerSupply psu{bus, PSUInventoryPath, 3, 0x68, PSUGPIOLineName};
465     MockedGPIOInterface* mockPresenceGPIO =
466         static_cast<MockedGPIOInterface*>(psu.getPresenceGPIO());
467     EXPECT_EQ(psu.isPresent(), false);
468 
469     // Change GPIO read to return 1 to indicate present.
470     EXPECT_CALL(*mockPresenceGPIO, read()).Times(1).WillOnce(Return(1));
471     psu.analyze();
472     EXPECT_EQ(psu.isPresent(), true);
473 }
474 
475 TEST_F(PowerSupplyTests, IsFaulted)
476 {
477     auto bus = sdbusplus::bus::new_default();
478 
479     PowerSupply psu{bus, PSUInventoryPath, 11, 0x6f, PSUGPIOLineName};
480     MockedGPIOInterface* mockPresenceGPIO =
481         static_cast<MockedGPIOInterface*>(psu.getPresenceGPIO());
482     // Always return 1 to indicate present.
483     EXPECT_CALL(*mockPresenceGPIO, read()).WillRepeatedly(Return(1));
484     psu.analyze();
485     EXPECT_EQ(psu.isFaulted(), false);
486     MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus());
487     // STATUS_WORD with fault bits on.
488     EXPECT_CALL(mockPMBus, read(STATUS_WORD, _))
489         .Times(1)
490         .WillOnce(Return(0xFFFF));
491     // Fault bit(s) on in STATUS_WORD causes read of STATUS_MFR_SPECIFIC,
492     // STATUS_INPUT, and STATUS_CML.
493     // STATUS_INPUT with fault bits on.
494     EXPECT_CALL(mockPMBus, read(STATUS_INPUT, _))
495         .Times(1)
496         .WillOnce(Return(0xFF));
497     // STATUS_MFR_SPECIFIC with faults bits on.
498     EXPECT_CALL(mockPMBus, read(STATUS_MFR, _)).Times(1).WillOnce(Return(0xFF));
499     // STATUS_CML with faults bits on.
500     EXPECT_CALL(mockPMBus, read(STATUS_CML, _)).Times(1).WillOnce(Return(0xFF));
501     psu.analyze();
502     EXPECT_EQ(psu.isFaulted(), true);
503 }
504 
505 TEST_F(PowerSupplyTests, HasInputFault)
506 {
507     auto bus = sdbusplus::bus::new_default();
508 
509     PowerSupply psu{bus, PSUInventoryPath, 3, 0x68, PSUGPIOLineName};
510     MockedGPIOInterface* mockPresenceGPIO =
511         static_cast<MockedGPIOInterface*>(psu.getPresenceGPIO());
512     // Always return 1 to indicate present.
513     EXPECT_CALL(*mockPresenceGPIO, read()).WillRepeatedly(Return(1));
514     psu.analyze();
515     MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus());
516     EXPECT_EQ(psu.hasInputFault(), false);
517     EXPECT_CALL(mockPMBus, read(_, _)).Times(1).WillOnce(Return(0x0000));
518     psu.analyze();
519     EXPECT_EQ(psu.hasInputFault(), false);
520     // STATUS_WORD with input fault/warn on.
521     EXPECT_CALL(mockPMBus, read(STATUS_WORD, _))
522         .Times(1)
523         .WillOnce(Return(status_word::INPUT_FAULT_WARN));
524     // Fault bit(s) on in STATUS_WORD causes read of STATUS_MFR_SPECIFIC,
525     // STATUS_INPUT, and STATUS_CML.
526     // STATUS_INPUT with an input fault bit on.
527     EXPECT_CALL(mockPMBus, read(STATUS_INPUT, _))
528         .Times(1)
529         .WillOnce(Return(0x80));
530     // STATUS_MFR don't care.
531     EXPECT_CALL(mockPMBus, read(STATUS_MFR, _)).Times(1).WillOnce(Return(0x00));
532     // STATUS_CML don't care.
533     EXPECT_CALL(mockPMBus, read(STATUS_CML, _)).Times(1).WillOnce(Return(0x00));
534     psu.analyze();
535     EXPECT_EQ(psu.hasInputFault(), true);
536     // STATUS_WORD with no bits on.
537     EXPECT_CALL(mockPMBus, read(STATUS_WORD, _))
538         .Times(1)
539         .WillOnce(Return(0x0000));
540     psu.analyze();
541     EXPECT_EQ(psu.hasInputFault(), false);
542 }
543 
544 TEST_F(PowerSupplyTests, HasMFRFault)
545 {
546     auto bus = sdbusplus::bus::new_default();
547 
548     PowerSupply psu{bus, PSUInventoryPath, 3, 0x68, PSUGPIOLineName};
549     MockedGPIOInterface* mockPresenceGPIO =
550         static_cast<MockedGPIOInterface*>(psu.getPresenceGPIO());
551     // Always return 1 to indicate present.
552     EXPECT_CALL(*mockPresenceGPIO, read()).WillRepeatedly(Return(1));
553     psu.analyze();
554     MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus());
555     EXPECT_EQ(psu.hasMFRFault(), false);
556     // First return STATUS_WORD with no bits on.
557     EXPECT_CALL(mockPMBus, read(STATUS_WORD, _))
558         .Times(1)
559         .WillOnce(Return(0x0000));
560     psu.analyze();
561     EXPECT_EQ(psu.hasMFRFault(), false);
562     // Next return STATUS_WORD with MFR fault bit on.
563     EXPECT_CALL(mockPMBus, read(STATUS_WORD, _))
564         .Times(1)
565         .WillOnce(Return(status_word::MFR_SPECIFIC_FAULT));
566     // Fault bit(s) on in STATUS_WORD causes read of STATUS_MFR_SPECIFIC,
567     // STATUS_INPUT, and STATUS_CML.
568     // STATUS_INPUT don't care
569     EXPECT_CALL(mockPMBus, read(STATUS_INPUT, _))
570         .Times(1)
571         .WillOnce(Return(0x00));
572     // STATUS_MFR_SPEFIC with bit(s) on.
573     EXPECT_CALL(mockPMBus, read(STATUS_MFR, _)).Times(1).WillOnce(Return(0xFF));
574     // STATUS_CML don't care.
575     EXPECT_CALL(mockPMBus, read(STATUS_CML, _)).Times(1).WillOnce(Return(0x00));
576     psu.analyze();
577     EXPECT_EQ(psu.hasMFRFault(), true);
578     // Back to no bits on in STATUS_WORD
579     EXPECT_CALL(mockPMBus, read(STATUS_WORD, _))
580         .Times(1)
581         .WillOnce(Return(0x0000));
582     psu.analyze();
583     EXPECT_EQ(psu.hasMFRFault(), false);
584 }
585 
586 TEST_F(PowerSupplyTests, HasVINUVFault)
587 {
588     auto bus = sdbusplus::bus::new_default();
589 
590     PowerSupply psu{bus, PSUInventoryPath, 3, 0x68, PSUGPIOLineName};
591     MockedGPIOInterface* mockPresenceGPIO =
592         static_cast<MockedGPIOInterface*>(psu.getPresenceGPIO());
593     // Always return 1 to indicate present.
594     EXPECT_CALL(*mockPresenceGPIO, read()).WillRepeatedly(Return(1));
595     psu.analyze();
596     MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus());
597     EXPECT_EQ(psu.hasVINUVFault(), false);
598     EXPECT_CALL(mockPMBus, read(STATUS_WORD, _))
599         .Times(1)
600         .WillOnce(Return(0x0000));
601     psu.analyze();
602     EXPECT_EQ(psu.hasVINUVFault(), false);
603     // Turn fault on.
604     EXPECT_CALL(mockPMBus, read(STATUS_WORD, _))
605         .Times(1)
606         .WillOnce(Return(status_word::VIN_UV_FAULT));
607     // Fault bit(s) on in STATUS_WORD causes read of STATUS_MFR_SPECIFIC,
608     // STATUS_INPUT, and STATUS_CML.
609     // Curious disagreement between PMBus Spec. Part II Figure 16 and 33. Go by
610     // Figure 16, and assume bits on in STATUS_INPUT.
611     EXPECT_CALL(mockPMBus, read(STATUS_INPUT, _))
612         .Times(1)
613         .WillOnce(Return(0x18));
614     // STATUS_MFR don't care.
615     EXPECT_CALL(mockPMBus, read(STATUS_MFR, _)).Times(1).WillOnce(Return(0x00));
616     // STATUS_CML don't care.
617     EXPECT_CALL(mockPMBus, read(STATUS_CML, _)).Times(1).WillOnce(Return(0x00));
618 
619     psu.analyze();
620     EXPECT_EQ(psu.hasVINUVFault(), true);
621     // Back to no fault bits on in STATUS_WORD
622     EXPECT_CALL(mockPMBus, read(STATUS_WORD, _))
623         .Times(1)
624         .WillOnce(Return(0x0000));
625     psu.analyze();
626     EXPECT_EQ(psu.hasVINUVFault(), false);
627 }
628