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