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 #if IBM_VPD
290         EXPECT_CALL(mockPMBus, readString(_, _))
291             .WillOnce(Return("CCIN"))
292             .WillOnce(Return("PN3456"))
293             .WillOnce(Return("FN3456"))
294             .WillOnce(Return("HEADER"))
295             .WillOnce(Return("SN3456"))
296             .WillOnce(Return("FW3456"));
297 #endif
298         psu.updateInventory();
299         // TODO: D-Bus mocking to verify values stored on D-Bus (???)
300     }
301     catch (...)
302     {
303         ADD_FAILURE() << "Should not have caught exception.";
304     }
305 }
306 
307 TEST_F(PowerSupplyTests, IsPresent)
308 {
309     auto bus = sdbusplus::bus::new_default();
310     EXPECT_CALL(mockedUtil, getPresence(_, StrEq(PSUInventoryPath))).Times(1);
311     PowerSupply psu{bus, PSUInventoryPath, 3, 0x68};
312     EXPECT_EQ(psu.isPresent(), false);
313 
314     EXPECT_CALL(mockedUtil, getPresence(_, _))
315         .WillOnce(Return(true)); // present
316     PowerSupply psu2{bus, PSUInventoryPath, 10, 0x6b};
317     EXPECT_EQ(psu2.isPresent(), true);
318 }
319 
320 TEST_F(PowerSupplyTests, IsFaulted)
321 {
322     auto bus = sdbusplus::bus::new_default();
323     EXPECT_CALL(mockedUtil, getPresence(_, _))
324         .WillOnce(Return(true)); // present
325     PowerSupply psu{bus, PSUInventoryPath, 11, 0x6f};
326     EXPECT_EQ(psu.isFaulted(), false);
327     MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus());
328     EXPECT_CALL(mockPMBus, read(_, _))
329         .Times(2)
330         .WillOnce(Return(0xFFFF))
331         .WillOnce(Return(1)); // mock return for read(STATUS_MFR... )
332     psu.analyze();
333     EXPECT_EQ(psu.isFaulted(), true);
334 }
335 
336 TEST_F(PowerSupplyTests, HasInputFault)
337 {
338     auto bus = sdbusplus::bus::new_default();
339     EXPECT_CALL(mockedUtil, getPresence(_, _))
340         .WillOnce(Return(true)); // present
341     PowerSupply psu{bus, PSUInventoryPath, 3, 0x68};
342     MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus());
343     EXPECT_EQ(psu.hasInputFault(), false);
344     EXPECT_CALL(mockPMBus, read(_, _)).Times(1).WillOnce(Return(0x0000));
345     psu.analyze();
346     EXPECT_EQ(psu.hasInputFault(), false);
347     EXPECT_CALL(mockPMBus, read(_, _))
348         .Times(2)
349         .WillOnce(Return(status_word::INPUT_FAULT_WARN))
350         .WillOnce(Return(0));
351     psu.analyze();
352     EXPECT_EQ(psu.hasInputFault(), true);
353     EXPECT_CALL(mockPMBus, read(_, _)).Times(1).WillOnce(Return(0x0000));
354     psu.analyze();
355     EXPECT_EQ(psu.hasInputFault(), false);
356 }
357 
358 TEST_F(PowerSupplyTests, HasMFRFault)
359 {
360     auto bus = sdbusplus::bus::new_default();
361     EXPECT_CALL(mockedUtil, getPresence(_, _))
362         .WillOnce(Return(true)); // present
363     PowerSupply psu{bus, PSUInventoryPath, 3, 0x68};
364     MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus());
365     EXPECT_EQ(psu.hasMFRFault(), false);
366     EXPECT_CALL(mockPMBus, read(_, _)).Times(1).WillOnce(Return(0x0000));
367     psu.analyze();
368     EXPECT_EQ(psu.hasMFRFault(), false);
369     EXPECT_CALL(mockPMBus, read(_, _))
370         .Times(2)
371         .WillOnce(Return(status_word::MFR_SPECIFIC_FAULT))
372         .WillOnce(Return(1)); // mock return for read(STATUS_MFR... )
373     psu.analyze();
374     EXPECT_EQ(psu.hasMFRFault(), true);
375     EXPECT_CALL(mockPMBus, read(_, _)).Times(1).WillOnce(Return(0x0000));
376     psu.analyze();
377     EXPECT_EQ(psu.hasMFRFault(), false);
378 }
379 
380 TEST_F(PowerSupplyTests, HasVINUVFault)
381 {
382     auto bus = sdbusplus::bus::new_default();
383     EXPECT_CALL(mockedUtil, getPresence(_, _))
384         .WillOnce(Return(true)); // present
385     PowerSupply psu{bus, PSUInventoryPath, 3, 0x68};
386     MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus());
387     EXPECT_EQ(psu.hasVINUVFault(), false);
388     EXPECT_CALL(mockPMBus, read(_, _)).Times(1).WillOnce(Return(0x0000));
389     psu.analyze();
390     EXPECT_EQ(psu.hasVINUVFault(), false);
391     EXPECT_CALL(mockPMBus, read(_, _))
392         .Times(2)
393         .WillOnce(Return(status_word::VIN_UV_FAULT))
394         .WillOnce(Return(0));
395     psu.analyze();
396     EXPECT_EQ(psu.hasVINUVFault(), true);
397     EXPECT_CALL(mockPMBus, read(_, _)).Times(1).WillOnce(Return(0x0000));
398     psu.analyze();
399     EXPECT_EQ(psu.hasVINUVFault(), false);
400 }
401