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, 0x68);
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, 0x68);
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, 0x69};
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, 0x6a};
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(2)
120         .WillOnce(Return(status_word::INPUT_FAULT_WARN))
121         .WillOnce(Return(0x0000));
122     psu2.analyze();
123     EXPECT_EQ(psu2.isPresent(), true);
124     EXPECT_EQ(psu2.isFaulted(), true);
125     EXPECT_EQ(psu2.hasInputFault(), true);
126     EXPECT_EQ(psu2.hasMFRFault(), false);
127     EXPECT_EQ(psu2.hasVINUVFault(), false);
128 
129     // STATUS_WORD INPUT/UV fault.
130     // First need it to return good status, then the fault
131     EXPECT_CALL(mockPMBus, read(_, _))
132         .WillOnce(Return(0x0000))
133         .WillOnce(Return(status_word::VIN_UV_FAULT))
134         .WillOnce(Return(0x0000));
135     psu2.analyze();
136     psu2.analyze();
137     EXPECT_EQ(psu2.isPresent(), true);
138     EXPECT_EQ(psu2.isFaulted(), true);
139     EXPECT_EQ(psu2.hasInputFault(), false);
140     EXPECT_EQ(psu2.hasMFRFault(), false);
141     EXPECT_EQ(psu2.hasVINUVFault(), true);
142 
143     // STATUS_WORD MFR fault.
144     EXPECT_CALL(mockPMBus, read(_, _))
145         .WillOnce(Return(0x0000))
146         .WillOnce(Return(status_word::MFR_SPECIFIC_FAULT))
147         .WillOnce(Return(1)); // mock return for read(STATUS_MFR... )
148     psu2.analyze();
149     psu2.analyze();
150     EXPECT_EQ(psu2.isPresent(), true);
151     EXPECT_EQ(psu2.isFaulted(), true);
152     EXPECT_EQ(psu2.hasInputFault(), false);
153     EXPECT_EQ(psu2.hasMFRFault(), true);
154     EXPECT_EQ(psu2.hasVINUVFault(), false);
155 
156     // Ignore Temperature fault.
157     EXPECT_CALL(mockPMBus, read(_, _))
158         .WillOnce(Return(0x0000))
159         .WillOnce(Return(status_word::TEMPERATURE_FAULT_WARN))
160         .WillOnce(Return(0x0000));
161     psu2.analyze();
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     // Ignore fan fault
170     EXPECT_CALL(mockPMBus, read(_, _))
171         .WillOnce(Return(0x0000))
172         .WillOnce(Return(status_word::FAN_FAULT))
173         .WillOnce(Return(0x0000));
174     psu2.analyze();
175     psu2.analyze();
176     EXPECT_EQ(psu2.isPresent(), true);
177     EXPECT_EQ(psu2.isFaulted(), false);
178     EXPECT_EQ(psu2.hasInputFault(), false);
179     EXPECT_EQ(psu2.hasMFRFault(), false);
180     EXPECT_EQ(psu2.hasVINUVFault(), false);
181 
182     // TODO: ReadFailure
183 }
184 
185 TEST_F(PowerSupplyTests, OnOffConfig)
186 {
187     auto bus = sdbusplus::bus::new_default();
188     uint8_t data = 0x15;
189 
190     // Test where PSU is NOT present
191     try
192     {
193         EXPECT_CALL(mockedUtil, getPresence(_, StrEq(PSUInventoryPath)))
194             .Times(1)
195             .WillOnce(Return(false));
196         PowerSupply psu{bus, PSUInventoryPath, 4, 0x69};
197         MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus());
198         // If it is not present, I should not be trying to write to it.
199         EXPECT_CALL(mockPMBus, writeBinary(_, _, _)).Times(0);
200         psu.onOffConfig(data);
201     }
202     catch (...)
203     {
204     }
205 
206     // Test where PSU is present
207     try
208     {
209         EXPECT_CALL(mockedUtil, getPresence(_, StrEq(PSUInventoryPath)))
210             .Times(1)
211             .WillOnce(Return(true)); // present
212         PowerSupply psu{bus, PSUInventoryPath, 5, 0x6a};
213         MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus());
214         // TODO: ???should I check the filename?
215         EXPECT_CALL(mockPMBus,
216                     writeBinary(_, ElementsAre(0x15), Type::HwmonDeviceDebug))
217             .Times(1);
218         psu.onOffConfig(data);
219     }
220     catch (...)
221     {
222     }
223 }
224 
225 TEST_F(PowerSupplyTests, ClearFaults)
226 {
227     auto bus = sdbusplus::bus::new_default();
228     EXPECT_CALL(mockedUtil, getPresence(_, StrEq(PSUInventoryPath)))
229         .Times(1)
230         .WillOnce(Return(true)); // present
231     PowerSupply psu{bus, PSUInventoryPath, 13, 0x68};
232     EXPECT_EQ(psu.isPresent(), true);
233     EXPECT_EQ(psu.isFaulted(), false);
234     EXPECT_EQ(psu.hasInputFault(), false);
235     EXPECT_EQ(psu.hasMFRFault(), false);
236     EXPECT_EQ(psu.hasVINUVFault(), false);
237     MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus());
238     EXPECT_CALL(mockPMBus, read(_, _))
239         .Times(2)
240         .WillOnce(Return(0xFFFF))
241         .WillOnce(Return(1)); // mock return for read(STATUS_MFR... )
242     psu.analyze();
243     EXPECT_EQ(psu.isPresent(), true);
244     EXPECT_EQ(psu.isFaulted(), true);
245     EXPECT_EQ(psu.hasInputFault(), true);
246     EXPECT_EQ(psu.hasMFRFault(), true);
247     EXPECT_EQ(psu.hasVINUVFault(), true);
248     EXPECT_CALL(mockPMBus, read("in1_input", _))
249         .Times(1)
250         .WillOnce(Return(209000));
251     psu.clearFaults();
252     EXPECT_EQ(psu.isPresent(), true);
253     EXPECT_EQ(psu.isFaulted(), false);
254     EXPECT_EQ(psu.hasInputFault(), false);
255     EXPECT_EQ(psu.hasMFRFault(), false);
256     EXPECT_EQ(psu.hasVINUVFault(), false);
257 }
258 
259 TEST_F(PowerSupplyTests, UpdateInventory)
260 {
261     auto bus = sdbusplus::bus::new_default();
262 
263     try
264     {
265         EXPECT_CALL(mockedUtil, getPresence(_, StrEq(PSUInventoryPath)))
266             .Times(1)
267             .WillOnce(Return(false)); // missing
268         PowerSupply psu{bus, PSUInventoryPath, 3, 0x68};
269         MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus());
270         // If it is not present, I should not be trying to read a string
271         EXPECT_CALL(mockPMBus, readString(_, _)).Times(0);
272         psu.updateInventory();
273     }
274     catch (...)
275     {
276         ADD_FAILURE() << "Should not have caught exception.";
277     }
278 
279     try
280     {
281         EXPECT_CALL(mockedUtil, getPresence(_, StrEq(PSUInventoryPath)))
282             .Times(1)
283             .WillOnce(Return(true)); // present
284         PowerSupply psu{bus, PSUInventoryPath, 13, 0x69};
285         MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus());
286         EXPECT_CALL(mockPMBus, readString(_, _)).WillRepeatedly(Return(""));
287         psu.updateInventory();
288 
289         EXPECT_CALL(mockPMBus, readString(_, _))
290             .WillOnce(Return("CCIN"))
291             .WillOnce(Return("PN3456"))
292             .WillOnce(Return("FN3456"))
293             .WillOnce(Return("HEADER"))
294             .WillOnce(Return("SN3456"))
295             .WillOnce(Return("FW3456"));
296         psu.updateInventory();
297         // TODO: D-Bus mocking to verify values stored on D-Bus (???)
298     }
299     catch (...)
300     {
301         ADD_FAILURE() << "Should not have caught exception.";
302     }
303 }
304 
305 TEST_F(PowerSupplyTests, IsPresent)
306 {
307     auto bus = sdbusplus::bus::new_default();
308     EXPECT_CALL(mockedUtil, getPresence(_, StrEq(PSUInventoryPath))).Times(1);
309     PowerSupply psu{bus, PSUInventoryPath, 3, 0x68};
310     EXPECT_EQ(psu.isPresent(), false);
311 
312     EXPECT_CALL(mockedUtil, getPresence(_, _))
313         .WillOnce(Return(true)); // present
314     PowerSupply psu2{bus, PSUInventoryPath, 10, 0x6b};
315     EXPECT_EQ(psu2.isPresent(), true);
316 }
317 
318 TEST_F(PowerSupplyTests, IsFaulted)
319 {
320     auto bus = sdbusplus::bus::new_default();
321     EXPECT_CALL(mockedUtil, getPresence(_, _))
322         .WillOnce(Return(true)); // present
323     PowerSupply psu{bus, PSUInventoryPath, 11, 0x6f};
324     EXPECT_EQ(psu.isFaulted(), false);
325     MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus());
326     EXPECT_CALL(mockPMBus, read(_, _))
327         .Times(2)
328         .WillOnce(Return(0xFFFF))
329         .WillOnce(Return(1)); // mock return for read(STATUS_MFR... )
330     psu.analyze();
331     EXPECT_EQ(psu.isFaulted(), true);
332 }
333 
334 TEST_F(PowerSupplyTests, HasInputFault)
335 {
336     auto bus = sdbusplus::bus::new_default();
337     EXPECT_CALL(mockedUtil, getPresence(_, _))
338         .WillOnce(Return(true)); // present
339     PowerSupply psu{bus, PSUInventoryPath, 3, 0x68};
340     MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus());
341     EXPECT_EQ(psu.hasInputFault(), false);
342     EXPECT_CALL(mockPMBus, read(_, _)).Times(1).WillOnce(Return(0x0000));
343     psu.analyze();
344     EXPECT_EQ(psu.hasInputFault(), false);
345     EXPECT_CALL(mockPMBus, read(_, _))
346         .Times(2)
347         .WillOnce(Return(status_word::INPUT_FAULT_WARN))
348         .WillOnce(Return(0));
349     psu.analyze();
350     EXPECT_EQ(psu.hasInputFault(), true);
351     EXPECT_CALL(mockPMBus, read(_, _)).Times(1).WillOnce(Return(0x0000));
352     psu.analyze();
353     EXPECT_EQ(psu.hasInputFault(), false);
354 }
355 
356 TEST_F(PowerSupplyTests, HasMFRFault)
357 {
358     auto bus = sdbusplus::bus::new_default();
359     EXPECT_CALL(mockedUtil, getPresence(_, _))
360         .WillOnce(Return(true)); // present
361     PowerSupply psu{bus, PSUInventoryPath, 3, 0x68};
362     MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus());
363     EXPECT_EQ(psu.hasMFRFault(), false);
364     EXPECT_CALL(mockPMBus, read(_, _)).Times(1).WillOnce(Return(0x0000));
365     psu.analyze();
366     EXPECT_EQ(psu.hasMFRFault(), false);
367     EXPECT_CALL(mockPMBus, read(_, _))
368         .Times(2)
369         .WillOnce(Return(status_word::MFR_SPECIFIC_FAULT))
370         .WillOnce(Return(1)); // mock return for read(STATUS_MFR... )
371     psu.analyze();
372     EXPECT_EQ(psu.hasMFRFault(), true);
373     EXPECT_CALL(mockPMBus, read(_, _)).Times(1).WillOnce(Return(0x0000));
374     psu.analyze();
375     EXPECT_EQ(psu.hasMFRFault(), false);
376 }
377 
378 TEST_F(PowerSupplyTests, HasVINUVFault)
379 {
380     auto bus = sdbusplus::bus::new_default();
381     EXPECT_CALL(mockedUtil, getPresence(_, _))
382         .WillOnce(Return(true)); // present
383     PowerSupply psu{bus, PSUInventoryPath, 3, 0x68};
384     MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus());
385     EXPECT_EQ(psu.hasVINUVFault(), false);
386     EXPECT_CALL(mockPMBus, read(_, _)).Times(1).WillOnce(Return(0x0000));
387     psu.analyze();
388     EXPECT_EQ(psu.hasVINUVFault(), false);
389     EXPECT_CALL(mockPMBus, read(_, _))
390         .Times(2)
391         .WillOnce(Return(status_word::VIN_UV_FAULT))
392         .WillOnce(Return(0));
393     psu.analyze();
394     EXPECT_EQ(psu.hasVINUVFault(), true);
395     EXPECT_CALL(mockPMBus, read(_, _)).Times(1).WillOnce(Return(0x0000));
396     psu.analyze();
397     EXPECT_EQ(psu.hasVINUVFault(), false);
398 }
399