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 
24 class PowerSupplyTests : public ::testing::Test
25 {
26   public:
27     PowerSupplyTests() :
28         mockedUtil(reinterpret_cast<const MockedUtil&>(getUtils()))
29     {
30         ON_CALL(mockedUtil, getPresence(_, _)).WillByDefault(Return(false));
31     }
32 
33     ~PowerSupplyTests() override
34     {
35         freeUtils();
36     }
37 
38     const MockedUtil& mockedUtil;
39 };
40 
41 TEST_F(PowerSupplyTests, Constructor)
42 {
43     /**
44      * @param[in] invpath - String for inventory path to use
45      * @param[in] i2cbus - The bus number this power supply is on
46      * @param[in] i2caddr - The 16-bit I2C address of the power supply
47      */
48     auto bus = sdbusplus::bus::new_default();
49 
50     // Try where inventory path is empty, constructor should fail.
51     try
52     {
53         auto psu = std::make_unique<PowerSupply>(bus, "", 3, "0068");
54         ADD_FAILURE() << "Should not have reached this line.";
55     }
56     catch (const std::invalid_argument& e)
57     {
58         EXPECT_STREQ(e.what(), "Invalid empty inventoryPath");
59     }
60     catch (...)
61     {
62         ADD_FAILURE() << "Should not have caught exception.";
63     }
64 
65     // Test with valid arguments
66     try
67     {
68         EXPECT_CALL(mockedUtil, getPresence(_, StrEq(PSUInventoryPath)))
69             .Times(1);
70         auto psu =
71             std::make_unique<PowerSupply>(bus, PSUInventoryPath, 3, "0068");
72 
73         EXPECT_EQ(psu->isPresent(), false);
74         EXPECT_EQ(psu->isFaulted(), false);
75         EXPECT_EQ(psu->hasInputFault(), false);
76         EXPECT_EQ(psu->hasMFRFault(), false);
77         EXPECT_EQ(psu->hasVINUVFault(), false);
78     }
79     catch (...)
80     {
81         ADD_FAILURE() << "Should not have caught exception.";
82     }
83 }
84 
85 TEST_F(PowerSupplyTests, Analyze)
86 {
87     auto bus = sdbusplus::bus::new_default();
88 
89     EXPECT_CALL(mockedUtil, getPresence(_, StrEq(PSUInventoryPath))).Times(1);
90     PowerSupply psu{bus, PSUInventoryPath, 4, "0069"};
91     psu.analyze();
92     // By default, nothing should change.
93     EXPECT_EQ(psu.isPresent(), false);
94     EXPECT_EQ(psu.isFaulted(), false);
95     EXPECT_EQ(psu.hasInputFault(), false);
96     EXPECT_EQ(psu.hasMFRFault(), false);
97     EXPECT_EQ(psu.hasVINUVFault(), false);
98 
99     // In order to get the various faults tested, the power supply needs to be
100     // present in order to read from the PMBus device(s).
101     EXPECT_CALL(mockedUtil, getPresence(_, StrEq(PSUInventoryPath)))
102         .Times(1)
103         .WillOnce(Return(true)); // present
104     PowerSupply psu2{bus, PSUInventoryPath, 5, "006a"};
105     EXPECT_EQ(psu2.isPresent(), true);
106 
107     // STATUS_WORD 0x0000 is powered on, no faults.
108     MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu2.getPMBus());
109     EXPECT_CALL(mockPMBus, read(_, _)).Times(1).WillOnce(Return(0x0000));
110     psu2.analyze();
111     EXPECT_EQ(psu2.isPresent(), true);
112     EXPECT_EQ(psu2.isFaulted(), false);
113     EXPECT_EQ(psu2.hasInputFault(), false);
114     EXPECT_EQ(psu2.hasMFRFault(), false);
115     EXPECT_EQ(psu2.hasVINUVFault(), false);
116 
117     // STATUS_WORD input fault/warn
118     EXPECT_CALL(mockPMBus, read(_, _))
119         .Times(1)
120         .WillOnce(Return(status_word::INPUT_FAULT_WARN));
121     psu2.analyze();
122     EXPECT_EQ(psu2.isPresent(), true);
123     EXPECT_EQ(psu2.isFaulted(), true);
124     EXPECT_EQ(psu2.hasInputFault(), true);
125     EXPECT_EQ(psu2.hasMFRFault(), false);
126     EXPECT_EQ(psu2.hasVINUVFault(), false);
127 
128     // STATUS_WORD INPUT/UV fault.
129     // First need it to return good status, then the fault
130     EXPECT_CALL(mockPMBus, read(_, _))
131         .WillOnce(Return(0x0000))
132         .WillOnce(Return(status_word::VIN_UV_FAULT));
133     psu2.analyze();
134     psu2.analyze();
135     EXPECT_EQ(psu2.isPresent(), true);
136     EXPECT_EQ(psu2.isFaulted(), true);
137     EXPECT_EQ(psu2.hasInputFault(), false);
138     EXPECT_EQ(psu2.hasMFRFault(), false);
139     EXPECT_EQ(psu2.hasVINUVFault(), true);
140 
141     // STATUS_WORD MFR fault.
142     EXPECT_CALL(mockPMBus, read(_, _))
143         .WillOnce(Return(0x0000))
144         .WillOnce(Return(status_word::MFR_SPECIFIC_FAULT));
145     psu2.analyze();
146     psu2.analyze();
147     EXPECT_EQ(psu2.isPresent(), true);
148     EXPECT_EQ(psu2.isFaulted(), true);
149     EXPECT_EQ(psu2.hasInputFault(), false);
150     EXPECT_EQ(psu2.hasMFRFault(), true);
151     EXPECT_EQ(psu2.hasVINUVFault(), false);
152 
153     // Ignore Temperature fault.
154     EXPECT_CALL(mockPMBus, read(_, _))
155         .WillOnce(Return(0x0000))
156         .WillOnce(Return(status_word::TEMPERATURE_FAULT_WARN));
157     psu2.analyze();
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     // Ignore fan fault
166     EXPECT_CALL(mockPMBus, read(_, _))
167         .WillOnce(Return(0x0000))
168         .WillOnce(Return(status_word::FAN_FAULT));
169     psu2.analyze();
170     psu2.analyze();
171     EXPECT_EQ(psu2.isPresent(), true);
172     EXPECT_EQ(psu2.isFaulted(), false);
173     EXPECT_EQ(psu2.hasInputFault(), false);
174     EXPECT_EQ(psu2.hasMFRFault(), false);
175     EXPECT_EQ(psu2.hasVINUVFault(), false);
176 
177     // TODO: ReadFailure
178 }
179 
180 TEST_F(PowerSupplyTests, OnOffConfig)
181 {
182     auto bus = sdbusplus::bus::new_default();
183     uint8_t data = 0x15;
184 
185     // Test where PSU is NOT present
186     try
187     {
188         EXPECT_CALL(mockedUtil, getPresence(_, StrEq(PSUInventoryPath)))
189             .Times(1)
190             .WillOnce(Return(false));
191         PowerSupply psu{bus, PSUInventoryPath, 4, "0069"};
192         MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus());
193         // If it is not present, I should not be trying to write to it.
194         EXPECT_CALL(mockPMBus, writeBinary(_, _, _)).Times(0);
195         psu.onOffConfig(data);
196     }
197     catch (...)
198     {
199     }
200 
201     // Test where PSU is present
202     try
203     {
204         EXPECT_CALL(mockedUtil, getPresence(_, StrEq(PSUInventoryPath)))
205             .Times(1)
206             .WillOnce(Return(true)); // present
207         PowerSupply psu{bus, PSUInventoryPath, 5, "006a"};
208         MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus());
209         // TODO: ???should I check the filename?
210         EXPECT_CALL(mockPMBus,
211                     writeBinary(_, ElementsAre(0x15), Type::HwmonDeviceDebug))
212             .Times(1);
213         psu.onOffConfig(data);
214     }
215     catch (...)
216     {
217     }
218 }
219 
220 TEST_F(PowerSupplyTests, ClearFaults)
221 {
222     auto bus = sdbusplus::bus::new_default();
223     EXPECT_CALL(mockedUtil, getPresence(_, StrEq(PSUInventoryPath)))
224         .Times(1)
225         .WillOnce(Return(true)); // present
226     PowerSupply psu{bus, PSUInventoryPath, 13, "0068"};
227     EXPECT_EQ(psu.isPresent(), true);
228     EXPECT_EQ(psu.isFaulted(), false);
229     EXPECT_EQ(psu.hasInputFault(), false);
230     EXPECT_EQ(psu.hasMFRFault(), false);
231     EXPECT_EQ(psu.hasVINUVFault(), false);
232     MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus());
233     EXPECT_CALL(mockPMBus, read(_, _)).Times(1).WillOnce(Return(0xFFFF));
234     psu.analyze();
235     EXPECT_EQ(psu.isPresent(), true);
236     EXPECT_EQ(psu.isFaulted(), true);
237     EXPECT_EQ(psu.hasInputFault(), true);
238     EXPECT_EQ(psu.hasMFRFault(), true);
239     EXPECT_EQ(psu.hasVINUVFault(), true);
240     EXPECT_CALL(mockPMBus, read("in1_input", _))
241         .Times(1)
242         .WillOnce(Return(209000));
243     psu.clearFaults();
244     EXPECT_EQ(psu.isPresent(), true);
245     EXPECT_EQ(psu.isFaulted(), false);
246     EXPECT_EQ(psu.hasInputFault(), false);
247     EXPECT_EQ(psu.hasMFRFault(), false);
248     EXPECT_EQ(psu.hasVINUVFault(), false);
249 }
250 
251 TEST_F(PowerSupplyTests, UpdateInventory)
252 {
253     auto bus = sdbusplus::bus::new_default();
254 
255     try
256     {
257         EXPECT_CALL(mockedUtil, getPresence(_, StrEq(PSUInventoryPath)))
258             .Times(1)
259             .WillOnce(Return(false)); // missing
260         PowerSupply psu{bus, PSUInventoryPath, 3, "0068"};
261         MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus());
262         // If it is not present, I should not be trying to read a string
263         EXPECT_CALL(mockPMBus, readString(_, _)).Times(0);
264         psu.updateInventory();
265     }
266     catch (...)
267     {
268         ADD_FAILURE() << "Should not have caught exception.";
269     }
270 
271     try
272     {
273         EXPECT_CALL(mockedUtil, getPresence(_, StrEq(PSUInventoryPath)))
274             .Times(1)
275             .WillOnce(Return(true)); // present
276         PowerSupply psu{bus, PSUInventoryPath, 13, "0069"};
277         MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus());
278         EXPECT_CALL(mockPMBus, readString(_, _)).WillRepeatedly(Return(""));
279         psu.updateInventory();
280 
281         EXPECT_CALL(mockPMBus, readString(_, _))
282             .WillOnce(Return("CCIN"))
283             .WillOnce(Return("PN3456"))
284             .WillOnce(Return("FN3456"))
285             .WillOnce(Return("HEADER"))
286             .WillOnce(Return("SN3456"))
287             .WillOnce(Return("FW3456"));
288         psu.updateInventory();
289         // TODO: D-Bus mocking to verify values stored on D-Bus (???)
290     }
291     catch (...)
292     {
293         ADD_FAILURE() << "Should not have caught exception.";
294     }
295 }
296 
297 TEST_F(PowerSupplyTests, IsPresent)
298 {
299     auto bus = sdbusplus::bus::new_default();
300     EXPECT_CALL(mockedUtil, getPresence(_, StrEq(PSUInventoryPath))).Times(1);
301     PowerSupply psu{bus, PSUInventoryPath, 3, "0068"};
302     EXPECT_EQ(psu.isPresent(), false);
303 
304     EXPECT_CALL(mockedUtil, getPresence(_, _))
305         .WillOnce(Return(true)); // present
306     PowerSupply psu2{bus, PSUInventoryPath, 10, "006b"};
307     EXPECT_EQ(psu2.isPresent(), true);
308 }
309 
310 TEST_F(PowerSupplyTests, IsFaulted)
311 {
312     auto bus = sdbusplus::bus::new_default();
313     EXPECT_CALL(mockedUtil, getPresence(_, _))
314         .WillOnce(Return(true)); // present
315     PowerSupply psu{bus, PSUInventoryPath, 11, "006f"};
316     EXPECT_EQ(psu.isFaulted(), false);
317     MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus());
318     EXPECT_CALL(mockPMBus, read(_, _)).Times(1).WillOnce(Return(0xFFFF));
319     psu.analyze();
320     EXPECT_EQ(psu.isFaulted(), true);
321 }
322 
323 TEST_F(PowerSupplyTests, HasInputFault)
324 {
325     auto bus = sdbusplus::bus::new_default();
326     EXPECT_CALL(mockedUtil, getPresence(_, _))
327         .WillOnce(Return(true)); // present
328     PowerSupply psu{bus, PSUInventoryPath, 3, "0068"};
329     MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus());
330     EXPECT_EQ(psu.hasInputFault(), false);
331     EXPECT_CALL(mockPMBus, read(_, _)).Times(1).WillOnce(Return(0x0000));
332     psu.analyze();
333     EXPECT_EQ(psu.hasInputFault(), false);
334     EXPECT_CALL(mockPMBus, read(_, _))
335         .Times(1)
336         .WillOnce(Return(status_word::INPUT_FAULT_WARN));
337     psu.analyze();
338     EXPECT_EQ(psu.hasInputFault(), true);
339     EXPECT_CALL(mockPMBus, read(_, _)).Times(1).WillOnce(Return(0x0000));
340     psu.analyze();
341     EXPECT_EQ(psu.hasInputFault(), false);
342 }
343 
344 TEST_F(PowerSupplyTests, HasMFRFault)
345 {
346     auto bus = sdbusplus::bus::new_default();
347     EXPECT_CALL(mockedUtil, getPresence(_, _))
348         .WillOnce(Return(true)); // present
349     PowerSupply psu{bus, PSUInventoryPath, 3, "0068"};
350     MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus());
351     EXPECT_EQ(psu.hasMFRFault(), false);
352     EXPECT_CALL(mockPMBus, read(_, _)).Times(1).WillOnce(Return(0x0000));
353     psu.analyze();
354     EXPECT_EQ(psu.hasMFRFault(), false);
355     EXPECT_CALL(mockPMBus, read(_, _))
356         .Times(1)
357         .WillOnce(Return(status_word::MFR_SPECIFIC_FAULT));
358     psu.analyze();
359     EXPECT_EQ(psu.hasMFRFault(), true);
360     EXPECT_CALL(mockPMBus, read(_, _)).Times(1).WillOnce(Return(0x0000));
361     psu.analyze();
362     EXPECT_EQ(psu.hasMFRFault(), false);
363 }
364 
365 TEST_F(PowerSupplyTests, HasVINUVFault)
366 {
367     auto bus = sdbusplus::bus::new_default();
368     EXPECT_CALL(mockedUtil, getPresence(_, _))
369         .WillOnce(Return(true)); // present
370     PowerSupply psu{bus, PSUInventoryPath, 3, "0068"};
371     MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus());
372     EXPECT_EQ(psu.hasVINUVFault(), false);
373     EXPECT_CALL(mockPMBus, read(_, _)).Times(1).WillOnce(Return(0x0000));
374     psu.analyze();
375     EXPECT_EQ(psu.hasVINUVFault(), false);
376     EXPECT_CALL(mockPMBus, read(_, _))
377         .Times(1)
378         .WillOnce(Return(status_word::VIN_UV_FAULT));
379     psu.analyze();
380     EXPECT_EQ(psu.hasVINUVFault(), true);
381     EXPECT_CALL(mockPMBus, read(_, _)).Times(1).WillOnce(Return(0x0000));
382     psu.analyze();
383     EXPECT_EQ(psu.hasVINUVFault(), false);
384 }
385