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 
257     // Test where PSU is present
258     try
259     {
260         // Assume GPIO presence, not inventory presence?
261         PowerSupply psu{bus, PSUInventoryPath, 5, 0x6a, PSUGPIOLineName};
262         MockedGPIOReader* mockPresenceGPIO =
263             static_cast<MockedGPIOReader*>(psu.getPresenceGPIO());
264         ON_CALL(*mockPresenceGPIO, read()).WillByDefault(Return(1));
265         MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus());
266         // TODO: expect setPresence call?
267         // updatePresence() private function reads gpio, called by analyze().
268         psu.analyze();
269         // TODO: ???should I check the filename?
270         EXPECT_CALL(mockPMBus,
271                     writeBinary(_, ElementsAre(0x15), Type::HwmonDeviceDebug))
272             .Times(1);
273         psu.onOffConfig(data);
274     }
275     catch (...)
276     {
277     }
278 }
279 
280 TEST_F(PowerSupplyTests, ClearFaults)
281 {
282     auto bus = sdbusplus::bus::new_default();
283     PowerSupply psu{bus, PSUInventoryPath, 13, 0x68, PSUGPIOLineName};
284     MockedGPIOReader* mockPresenceGPIO =
285         static_cast<MockedGPIOReader*>(psu.getPresenceGPIO());
286     // GPIO read return 1 to indicate present.
287     ON_CALL(*mockPresenceGPIO, read()).WillByDefault(Return(1));
288     MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus());
289     ON_CALL(mockPMBus, read(_, _)).WillByDefault(Return(0));
290     psu.analyze();
291     EXPECT_EQ(psu.isPresent(), true);
292     EXPECT_EQ(psu.isFaulted(), false);
293     EXPECT_EQ(psu.hasInputFault(), false);
294     EXPECT_EQ(psu.hasMFRFault(), false);
295     EXPECT_EQ(psu.hasVINUVFault(), false);
296     EXPECT_CALL(mockPMBus, read(_, _))
297         .Times(2)
298         .WillOnce(Return(0xFFFF))
299         .WillOnce(Return(1)); // mock return for read(STATUS_MFR... )
300     psu.analyze();
301     EXPECT_EQ(psu.isPresent(), true);
302     EXPECT_EQ(psu.isFaulted(), true);
303     EXPECT_EQ(psu.hasInputFault(), true);
304     EXPECT_EQ(psu.hasMFRFault(), true);
305     EXPECT_EQ(psu.hasVINUVFault(), true);
306     EXPECT_CALL(mockPMBus, read("in1_input", _))
307         .Times(1)
308         .WillOnce(Return(209000));
309     psu.clearFaults();
310     EXPECT_EQ(psu.isPresent(), true);
311     EXPECT_EQ(psu.isFaulted(), false);
312     EXPECT_EQ(psu.hasInputFault(), false);
313     EXPECT_EQ(psu.hasMFRFault(), false);
314     EXPECT_EQ(psu.hasVINUVFault(), false);
315 
316     // TODO: Faults clear on missing/present?
317 }
318 
319 TEST_F(PowerSupplyTests, UpdateInventory)
320 {
321     auto bus = sdbusplus::bus::new_default();
322 
323     try
324     {
325         PowerSupply psu{bus, PSUInventoryPath, 3, 0x68, PSUGPIOLineName};
326         MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus());
327         // If it is not present, I should not be trying to read a string
328         EXPECT_CALL(mockPMBus, readString(_, _)).Times(0);
329         psu.updateInventory();
330     }
331     catch (...)
332     {
333         ADD_FAILURE() << "Should not have caught exception.";
334     }
335 
336     try
337     {
338         PowerSupply psu{bus, PSUInventoryPath, 13, 0x69, PSUGPIOLineName};
339         MockedGPIOReader* mockPresenceGPIO =
340             static_cast<MockedGPIOReader*>(psu.getPresenceGPIO());
341         // GPIO read return 1 to indicate present.
342         EXPECT_CALL(*mockPresenceGPIO, read()).Times(1).WillOnce(Return(1));
343         psu.analyze();
344         MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus());
345         EXPECT_CALL(mockPMBus, readString(_, _)).WillRepeatedly(Return(""));
346         psu.updateInventory();
347 
348 #if IBM_VPD
349         EXPECT_CALL(mockPMBus, readString(_, _))
350             .WillOnce(Return("CCIN"))
351             .WillOnce(Return("PN3456"))
352             .WillOnce(Return("FN3456"))
353             .WillOnce(Return("HEADER"))
354             .WillOnce(Return("SN3456"))
355             .WillOnce(Return("FW3456"));
356 #endif
357         psu.updateInventory();
358         // TODO: D-Bus mocking to verify values stored on D-Bus (???)
359     }
360     catch (...)
361     {
362         ADD_FAILURE() << "Should not have caught exception.";
363     }
364 }
365 
366 TEST_F(PowerSupplyTests, IsPresent)
367 {
368     auto bus = sdbusplus::bus::new_default();
369 
370     PowerSupply psu{bus, PSUInventoryPath, 3, 0x68, PSUGPIOLineName};
371     MockedGPIOReader* mockPresenceGPIO =
372         static_cast<MockedGPIOReader*>(psu.getPresenceGPIO());
373     EXPECT_EQ(psu.isPresent(), false);
374 
375     // Change GPIO read to return 1 to indicate present.
376     EXPECT_CALL(*mockPresenceGPIO, read()).Times(1).WillOnce(Return(1));
377     psu.analyze();
378     EXPECT_EQ(psu.isPresent(), true);
379 }
380 
381 TEST_F(PowerSupplyTests, IsFaulted)
382 {
383     auto bus = sdbusplus::bus::new_default();
384 
385     PowerSupply psu{bus, PSUInventoryPath, 11, 0x6f, PSUGPIOLineName};
386     MockedGPIOReader* mockPresenceGPIO =
387         static_cast<MockedGPIOReader*>(psu.getPresenceGPIO());
388     // Always return 1 to indicate present.
389     EXPECT_CALL(*mockPresenceGPIO, read()).WillRepeatedly(Return(1));
390     psu.analyze();
391     EXPECT_EQ(psu.isFaulted(), false);
392     MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus());
393     EXPECT_CALL(mockPMBus, read(_, _))
394         .Times(2)
395         .WillOnce(Return(0xFFFF))
396         .WillOnce(Return(1)); // mock return for read(STATUS_MFR... )
397     psu.analyze();
398     EXPECT_EQ(psu.isFaulted(), true);
399 }
400 
401 TEST_F(PowerSupplyTests, HasInputFault)
402 {
403     auto bus = sdbusplus::bus::new_default();
404 
405     PowerSupply psu{bus, PSUInventoryPath, 3, 0x68, PSUGPIOLineName};
406     MockedGPIOReader* mockPresenceGPIO =
407         static_cast<MockedGPIOReader*>(psu.getPresenceGPIO());
408     // Always return 1 to indicate present.
409     EXPECT_CALL(*mockPresenceGPIO, read()).WillRepeatedly(Return(1));
410     psu.analyze();
411     MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus());
412     EXPECT_EQ(psu.hasInputFault(), false);
413     EXPECT_CALL(mockPMBus, read(_, _)).Times(1).WillOnce(Return(0x0000));
414     psu.analyze();
415     EXPECT_EQ(psu.hasInputFault(), false);
416     EXPECT_CALL(mockPMBus, read(_, _))
417         .Times(2)
418         .WillOnce(Return(status_word::INPUT_FAULT_WARN))
419         .WillOnce(Return(0));
420     psu.analyze();
421     EXPECT_EQ(psu.hasInputFault(), true);
422     EXPECT_CALL(mockPMBus, read(_, _)).Times(1).WillOnce(Return(0x0000));
423     psu.analyze();
424     EXPECT_EQ(psu.hasInputFault(), false);
425 }
426 
427 TEST_F(PowerSupplyTests, HasMFRFault)
428 {
429     auto bus = sdbusplus::bus::new_default();
430 
431     PowerSupply psu{bus, PSUInventoryPath, 3, 0x68, PSUGPIOLineName};
432     MockedGPIOReader* mockPresenceGPIO =
433         static_cast<MockedGPIOReader*>(psu.getPresenceGPIO());
434     // Always return 1 to indicate present.
435     EXPECT_CALL(*mockPresenceGPIO, read()).WillRepeatedly(Return(1));
436     psu.analyze();
437     MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus());
438     EXPECT_EQ(psu.hasMFRFault(), false);
439     EXPECT_CALL(mockPMBus, read(_, _)).Times(1).WillOnce(Return(0x0000));
440     psu.analyze();
441     EXPECT_EQ(psu.hasMFRFault(), false);
442     EXPECT_CALL(mockPMBus, read(_, _))
443         .Times(2)
444         .WillOnce(Return(status_word::MFR_SPECIFIC_FAULT))
445         .WillOnce(Return(1)); // mock return for read(STATUS_MFR... )
446     psu.analyze();
447     EXPECT_EQ(psu.hasMFRFault(), true);
448     EXPECT_CALL(mockPMBus, read(_, _)).Times(1).WillOnce(Return(0x0000));
449     psu.analyze();
450     EXPECT_EQ(psu.hasMFRFault(), false);
451 }
452 
453 TEST_F(PowerSupplyTests, HasVINUVFault)
454 {
455     auto bus = sdbusplus::bus::new_default();
456 
457     PowerSupply psu{bus, PSUInventoryPath, 3, 0x68, PSUGPIOLineName};
458     MockedGPIOReader* mockPresenceGPIO =
459         static_cast<MockedGPIOReader*>(psu.getPresenceGPIO());
460     // Always return 1 to indicate present.
461     EXPECT_CALL(*mockPresenceGPIO, read()).WillRepeatedly(Return(1));
462     psu.analyze();
463     MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus());
464     EXPECT_EQ(psu.hasVINUVFault(), false);
465     EXPECT_CALL(mockPMBus, read(_, _)).Times(1).WillOnce(Return(0x0000));
466     psu.analyze();
467     EXPECT_EQ(psu.hasVINUVFault(), false);
468     EXPECT_CALL(mockPMBus, read(_, _))
469         .Times(2)
470         .WillOnce(Return(status_word::VIN_UV_FAULT))
471         .WillOnce(Return(0));
472     psu.analyze();
473     EXPECT_EQ(psu.hasVINUVFault(), true);
474     EXPECT_CALL(mockPMBus, read(_, _)).Times(1).WillOnce(Return(0x0000));
475     psu.analyze();
476     EXPECT_EQ(psu.hasVINUVFault(), false);
477 }
478