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     MockedGPIOReader* mockPresenceGPIO =
134         static_cast<MockedGPIOReader*>(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     MockedGPIOReader* mockPresenceGPIO2 =
149         static_cast<MockedGPIOReader*>(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.
155     // It will read STATUS_MFR at the same time, so there are 2 reads.
156     MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu2.getPMBus());
157     EXPECT_CALL(mockPMBus, read(_, _)).Times(2).WillRepeatedly(Return(0x0000));
158     psu2.analyze();
159     EXPECT_EQ(psu2.isPresent(), true);
160     EXPECT_EQ(psu2.isFaulted(), false);
161     EXPECT_EQ(psu2.hasInputFault(), false);
162     EXPECT_EQ(psu2.hasMFRFault(), false);
163     EXPECT_EQ(psu2.hasVINUVFault(), false);
164 
165     // STATUS_WORD input fault/warn
166     EXPECT_CALL(mockPMBus, read(_, _))
167         .Times(2)
168         .WillOnce(Return(status_word::INPUT_FAULT_WARN))
169         .WillOnce(Return(0x0000));
170     psu2.analyze();
171     EXPECT_EQ(psu2.isPresent(), true);
172     EXPECT_EQ(psu2.isFaulted(), true);
173     EXPECT_EQ(psu2.hasInputFault(), true);
174     EXPECT_EQ(psu2.hasMFRFault(), false);
175     EXPECT_EQ(psu2.hasVINUVFault(), false);
176 
177     // STATUS_WORD INPUT/UV fault.
178     // First need it to return good status, then the fault
179     EXPECT_CALL(mockPMBus, read(_, _))
180         .WillOnce(Return(0x0000))
181         .WillOnce(Return(status_word::VIN_UV_FAULT))
182         .WillOnce(Return(0x0000));
183     psu2.analyze();
184     psu2.analyze();
185     EXPECT_EQ(psu2.isPresent(), true);
186     EXPECT_EQ(psu2.isFaulted(), true);
187     EXPECT_EQ(psu2.hasInputFault(), false);
188     EXPECT_EQ(psu2.hasMFRFault(), false);
189     EXPECT_EQ(psu2.hasVINUVFault(), true);
190 
191     // STATUS_WORD MFR fault.
192     EXPECT_CALL(mockPMBus, read(_, _))
193         .WillOnce(Return(0x0000))
194         .WillOnce(Return(status_word::MFR_SPECIFIC_FAULT))
195         .WillOnce(Return(1)); // mock return for read(STATUS_MFR... )
196     psu2.analyze();
197     psu2.analyze();
198     EXPECT_EQ(psu2.isPresent(), true);
199     EXPECT_EQ(psu2.isFaulted(), true);
200     EXPECT_EQ(psu2.hasInputFault(), false);
201     EXPECT_EQ(psu2.hasMFRFault(), true);
202     EXPECT_EQ(psu2.hasVINUVFault(), false);
203 
204     // Ignore Temperature fault.
205     EXPECT_CALL(mockPMBus, read(_, _))
206         .WillOnce(Return(0x0000))
207         .WillOnce(Return(status_word::TEMPERATURE_FAULT_WARN))
208         .WillOnce(Return(0x0000));
209     psu2.analyze();
210     psu2.analyze();
211     EXPECT_EQ(psu2.isPresent(), true);
212     EXPECT_EQ(psu2.isFaulted(), false);
213     EXPECT_EQ(psu2.hasInputFault(), false);
214     EXPECT_EQ(psu2.hasMFRFault(), false);
215     EXPECT_EQ(psu2.hasVINUVFault(), false);
216 
217     // Ignore fan fault
218     EXPECT_CALL(mockPMBus, read(_, _))
219         .WillOnce(Return(0x0000))
220         .WillOnce(Return(status_word::FAN_FAULT))
221         .WillOnce(Return(0x0000));
222     psu2.analyze();
223     psu2.analyze();
224     EXPECT_EQ(psu2.isPresent(), true);
225     EXPECT_EQ(psu2.isFaulted(), false);
226     EXPECT_EQ(psu2.hasInputFault(), false);
227     EXPECT_EQ(psu2.hasMFRFault(), false);
228     EXPECT_EQ(psu2.hasVINUVFault(), false);
229 
230     // TODO: ReadFailure
231 }
232 
233 TEST_F(PowerSupplyTests, OnOffConfig)
234 {
235     auto bus = sdbusplus::bus::new_default();
236     uint8_t data = 0x15;
237 
238     // Test where PSU is NOT present
239     try
240     {
241         // Assume GPIO presence, not inventory presence?
242         PowerSupply psu{bus, PSUInventoryPath, 4, 0x69, PSUGPIOLineName};
243 
244         MockedGPIOReader* mockPresenceGPIO =
245             static_cast<MockedGPIOReader*>(psu.getPresenceGPIO());
246         ON_CALL(*mockPresenceGPIO, read()).WillByDefault(Return(0));
247         MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus());
248         // Constructor should set initial presence, default read returns 0.
249         // If it is not present, I should not be trying to write to it.
250         EXPECT_CALL(mockPMBus, writeBinary(_, _, _)).Times(0);
251         psu.onOffConfig(data);
252     }
253     catch (...)
254     {}
255 
256     // Test where PSU is present
257     try
258     {
259         // Assume GPIO presence, not inventory presence?
260         PowerSupply psu{bus, PSUInventoryPath, 5, 0x6a, PSUGPIOLineName};
261         MockedGPIOReader* mockPresenceGPIO =
262             static_cast<MockedGPIOReader*>(psu.getPresenceGPIO());
263         ON_CALL(*mockPresenceGPIO, read()).WillByDefault(Return(1));
264         MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus());
265         // TODO: expect setPresence call?
266         // updatePresence() private function reads gpio, called by analyze().
267         psu.analyze();
268         // TODO: ???should I check the filename?
269         EXPECT_CALL(mockPMBus,
270                     writeBinary(_, ElementsAre(0x15), Type::HwmonDeviceDebug))
271             .Times(1);
272         psu.onOffConfig(data);
273     }
274     catch (...)
275     {}
276 }
277 
278 TEST_F(PowerSupplyTests, ClearFaults)
279 {
280     auto bus = sdbusplus::bus::new_default();
281     PowerSupply psu{bus, PSUInventoryPath, 13, 0x68, PSUGPIOLineName};
282     MockedGPIOReader* mockPresenceGPIO =
283         static_cast<MockedGPIOReader*>(psu.getPresenceGPIO());
284     // GPIO read return 1 to indicate present.
285     ON_CALL(*mockPresenceGPIO, read()).WillByDefault(Return(1));
286     MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus());
287     ON_CALL(mockPMBus, read(_, _)).WillByDefault(Return(0));
288     psu.analyze();
289     EXPECT_EQ(psu.isPresent(), true);
290     EXPECT_EQ(psu.isFaulted(), false);
291     EXPECT_EQ(psu.hasInputFault(), false);
292     EXPECT_EQ(psu.hasMFRFault(), false);
293     EXPECT_EQ(psu.hasVINUVFault(), false);
294     EXPECT_CALL(mockPMBus, read(_, _))
295         .Times(2)
296         .WillOnce(Return(0xFFFF))
297         .WillOnce(Return(1)); // mock return for read(STATUS_MFR... )
298     psu.analyze();
299     EXPECT_EQ(psu.isPresent(), true);
300     EXPECT_EQ(psu.isFaulted(), true);
301     EXPECT_EQ(psu.hasInputFault(), true);
302     EXPECT_EQ(psu.hasMFRFault(), true);
303     EXPECT_EQ(psu.hasVINUVFault(), true);
304     EXPECT_CALL(mockPMBus, read("in1_input", _))
305         .Times(1)
306         .WillOnce(Return(209000));
307     psu.clearFaults();
308     EXPECT_EQ(psu.isPresent(), true);
309     EXPECT_EQ(psu.isFaulted(), false);
310     EXPECT_EQ(psu.hasInputFault(), false);
311     EXPECT_EQ(psu.hasMFRFault(), false);
312     EXPECT_EQ(psu.hasVINUVFault(), false);
313 
314     // TODO: Faults clear on missing/present?
315 }
316 
317 TEST_F(PowerSupplyTests, UpdateInventory)
318 {
319     auto bus = sdbusplus::bus::new_default();
320 
321     try
322     {
323         PowerSupply psu{bus, PSUInventoryPath, 3, 0x68, PSUGPIOLineName};
324         MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus());
325         // If it is not present, I should not be trying to read a string
326         EXPECT_CALL(mockPMBus, readString(_, _)).Times(0);
327         psu.updateInventory();
328     }
329     catch (...)
330     {
331         ADD_FAILURE() << "Should not have caught exception.";
332     }
333 
334     try
335     {
336         PowerSupply psu{bus, PSUInventoryPath, 13, 0x69, PSUGPIOLineName};
337         MockedGPIOReader* mockPresenceGPIO =
338             static_cast<MockedGPIOReader*>(psu.getPresenceGPIO());
339         // GPIO read return 1 to indicate present.
340         EXPECT_CALL(*mockPresenceGPIO, read()).Times(1).WillOnce(Return(1));
341         psu.analyze();
342         MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus());
343         EXPECT_CALL(mockPMBus, readString(_, _)).WillRepeatedly(Return(""));
344         psu.updateInventory();
345 
346 #if IBM_VPD
347         EXPECT_CALL(mockPMBus, readString(_, _))
348             .WillOnce(Return("CCIN"))
349             .WillOnce(Return("PN3456"))
350             .WillOnce(Return("FN3456"))
351             .WillOnce(Return("HEADER"))
352             .WillOnce(Return("SN3456"))
353             .WillOnce(Return("FW3456"));
354 #endif
355         psu.updateInventory();
356         // TODO: D-Bus mocking to verify values stored on D-Bus (???)
357     }
358     catch (...)
359     {
360         ADD_FAILURE() << "Should not have caught exception.";
361     }
362 }
363 
364 TEST_F(PowerSupplyTests, IsPresent)
365 {
366     auto bus = sdbusplus::bus::new_default();
367 
368     PowerSupply psu{bus, PSUInventoryPath, 3, 0x68, PSUGPIOLineName};
369     MockedGPIOReader* mockPresenceGPIO =
370         static_cast<MockedGPIOReader*>(psu.getPresenceGPIO());
371     EXPECT_EQ(psu.isPresent(), false);
372 
373     // Change GPIO read to return 1 to indicate present.
374     EXPECT_CALL(*mockPresenceGPIO, read()).Times(1).WillOnce(Return(1));
375     psu.analyze();
376     EXPECT_EQ(psu.isPresent(), true);
377 }
378 
379 TEST_F(PowerSupplyTests, IsFaulted)
380 {
381     auto bus = sdbusplus::bus::new_default();
382 
383     PowerSupply psu{bus, PSUInventoryPath, 11, 0x6f, PSUGPIOLineName};
384     MockedGPIOReader* mockPresenceGPIO =
385         static_cast<MockedGPIOReader*>(psu.getPresenceGPIO());
386     // Always return 1 to indicate present.
387     EXPECT_CALL(*mockPresenceGPIO, read()).WillRepeatedly(Return(1));
388     psu.analyze();
389     EXPECT_EQ(psu.isFaulted(), false);
390     MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus());
391     EXPECT_CALL(mockPMBus, read(_, _))
392         .Times(2)
393         .WillOnce(Return(0xFFFF))
394         .WillOnce(Return(1)); // mock return for read(STATUS_MFR... )
395     psu.analyze();
396     EXPECT_EQ(psu.isFaulted(), true);
397 }
398 
399 TEST_F(PowerSupplyTests, HasInputFault)
400 {
401     auto bus = sdbusplus::bus::new_default();
402 
403     PowerSupply psu{bus, PSUInventoryPath, 3, 0x68, PSUGPIOLineName};
404     MockedGPIOReader* mockPresenceGPIO =
405         static_cast<MockedGPIOReader*>(psu.getPresenceGPIO());
406     // Always return 1 to indicate present.
407     EXPECT_CALL(*mockPresenceGPIO, read()).WillRepeatedly(Return(1));
408     psu.analyze();
409     MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus());
410     EXPECT_EQ(psu.hasInputFault(), false);
411     EXPECT_CALL(mockPMBus, read(_, _)).Times(1).WillOnce(Return(0x0000));
412     psu.analyze();
413     EXPECT_EQ(psu.hasInputFault(), false);
414     EXPECT_CALL(mockPMBus, read(_, _))
415         .Times(2)
416         .WillOnce(Return(status_word::INPUT_FAULT_WARN))
417         .WillOnce(Return(0));
418     psu.analyze();
419     EXPECT_EQ(psu.hasInputFault(), true);
420     EXPECT_CALL(mockPMBus, read(_, _)).Times(1).WillOnce(Return(0x0000));
421     psu.analyze();
422     EXPECT_EQ(psu.hasInputFault(), false);
423 }
424 
425 TEST_F(PowerSupplyTests, HasMFRFault)
426 {
427     auto bus = sdbusplus::bus::new_default();
428 
429     PowerSupply psu{bus, PSUInventoryPath, 3, 0x68, PSUGPIOLineName};
430     MockedGPIOReader* mockPresenceGPIO =
431         static_cast<MockedGPIOReader*>(psu.getPresenceGPIO());
432     // Always return 1 to indicate present.
433     EXPECT_CALL(*mockPresenceGPIO, read()).WillRepeatedly(Return(1));
434     psu.analyze();
435     MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus());
436     EXPECT_EQ(psu.hasMFRFault(), false);
437     EXPECT_CALL(mockPMBus, read(_, _)).Times(1).WillOnce(Return(0x0000));
438     psu.analyze();
439     EXPECT_EQ(psu.hasMFRFault(), false);
440     EXPECT_CALL(mockPMBus, read(_, _))
441         .Times(2)
442         .WillOnce(Return(status_word::MFR_SPECIFIC_FAULT))
443         .WillOnce(Return(1)); // mock return for read(STATUS_MFR... )
444     psu.analyze();
445     EXPECT_EQ(psu.hasMFRFault(), true);
446     EXPECT_CALL(mockPMBus, read(_, _)).Times(1).WillOnce(Return(0x0000));
447     psu.analyze();
448     EXPECT_EQ(psu.hasMFRFault(), false);
449 }
450 
451 TEST_F(PowerSupplyTests, HasVINUVFault)
452 {
453     auto bus = sdbusplus::bus::new_default();
454 
455     PowerSupply psu{bus, PSUInventoryPath, 3, 0x68, PSUGPIOLineName};
456     MockedGPIOReader* mockPresenceGPIO =
457         static_cast<MockedGPIOReader*>(psu.getPresenceGPIO());
458     // Always return 1 to indicate present.
459     EXPECT_CALL(*mockPresenceGPIO, read()).WillRepeatedly(Return(1));
460     psu.analyze();
461     MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus());
462     EXPECT_EQ(psu.hasVINUVFault(), false);
463     EXPECT_CALL(mockPMBus, read(_, _)).Times(1).WillOnce(Return(0x0000));
464     psu.analyze();
465     EXPECT_EQ(psu.hasVINUVFault(), false);
466     EXPECT_CALL(mockPMBus, read(_, _))
467         .Times(2)
468         .WillOnce(Return(status_word::VIN_UV_FAULT))
469         .WillOnce(Return(0));
470     psu.analyze();
471     EXPECT_EQ(psu.hasVINUVFault(), true);
472     EXPECT_CALL(mockPMBus, read(_, _)).Times(1).WillOnce(Return(0x0000));
473     psu.analyze();
474     EXPECT_EQ(psu.hasVINUVFault(), false);
475 }
476