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