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     EXPECT_CALL(mockedUtil, getPresence(_, StrEq(PSUInventoryPath))).Times(1);
50     auto psu = std::make_unique<PowerSupply>(bus, PSUInventoryPath, 3, "0068");
51 
52     EXPECT_EQ(psu->isPresent(), false);
53     EXPECT_EQ(psu->isFaulted(), false);
54     EXPECT_EQ(psu->hasInputFault(), false);
55     EXPECT_EQ(psu->hasMFRFault(), false);
56     EXPECT_EQ(psu->hasVINUVFault(), false);
57 }
58 
59 TEST_F(PowerSupplyTests, Analyze)
60 {
61     auto bus = sdbusplus::bus::new_default();
62 
63     EXPECT_CALL(mockedUtil, getPresence(_, StrEq(PSUInventoryPath))).Times(1);
64     PowerSupply psu{bus, PSUInventoryPath, 4, "0069"};
65     psu.analyze();
66     // By default, nothing should change.
67     EXPECT_EQ(psu.isPresent(), false);
68     EXPECT_EQ(psu.isFaulted(), false);
69     EXPECT_EQ(psu.hasInputFault(), false);
70     EXPECT_EQ(psu.hasMFRFault(), false);
71     EXPECT_EQ(psu.hasVINUVFault(), false);
72 
73     // In order to get the various faults tested, the power supply needs to be
74     // present in order to read from the PMBus device(s).
75     EXPECT_CALL(mockedUtil, getPresence(_, StrEq(PSUInventoryPath)))
76         .Times(1)
77         .WillOnce(Return(true)); // present
78     PowerSupply psu2{bus, PSUInventoryPath, 5, "006a"};
79     EXPECT_EQ(psu2.isPresent(), true);
80 
81     // STATUS_WORD 0x0000 is powered on, no faults.
82     MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu2.getPMBus());
83     EXPECT_CALL(mockPMBus, read(_, _)).Times(1).WillOnce(Return(0x0000));
84     psu2.analyze();
85     EXPECT_EQ(psu2.isPresent(), true);
86     EXPECT_EQ(psu2.isFaulted(), false);
87     EXPECT_EQ(psu2.hasInputFault(), false);
88     EXPECT_EQ(psu2.hasMFRFault(), false);
89     EXPECT_EQ(psu2.hasVINUVFault(), false);
90 
91     // STATUS_WORD input fault/warn
92     EXPECT_CALL(mockPMBus, read(_, _))
93         .Times(1)
94         .WillOnce(Return(status_word::INPUT_FAULT_WARN));
95     psu2.analyze();
96     EXPECT_EQ(psu2.isPresent(), true);
97     EXPECT_EQ(psu2.isFaulted(), true);
98     EXPECT_EQ(psu2.hasInputFault(), true);
99     EXPECT_EQ(psu2.hasMFRFault(), false);
100     EXPECT_EQ(psu2.hasVINUVFault(), false);
101 
102     // STATUS_WORD INPUT/UV fault.
103     // First need it to return good status, then the fault
104     EXPECT_CALL(mockPMBus, read(_, _))
105         .WillOnce(Return(0x0000))
106         .WillOnce(Return(status_word::VIN_UV_FAULT));
107     psu2.analyze();
108     psu2.analyze();
109     EXPECT_EQ(psu2.isPresent(), true);
110     EXPECT_EQ(psu2.isFaulted(), true);
111     EXPECT_EQ(psu2.hasInputFault(), false);
112     EXPECT_EQ(psu2.hasMFRFault(), false);
113     EXPECT_EQ(psu2.hasVINUVFault(), true);
114 
115     // STATUS_WORD MFR fault.
116     EXPECT_CALL(mockPMBus, read(_, _))
117         .WillOnce(Return(0x0000))
118         .WillOnce(Return(status_word::MFR_SPECIFIC_FAULT));
119     psu2.analyze();
120     psu2.analyze();
121     EXPECT_EQ(psu2.isPresent(), true);
122     EXPECT_EQ(psu2.isFaulted(), true);
123     EXPECT_EQ(psu2.hasInputFault(), false);
124     EXPECT_EQ(psu2.hasMFRFault(), true);
125     EXPECT_EQ(psu2.hasVINUVFault(), false);
126 
127     // Ignore Temperature fault.
128     EXPECT_CALL(mockPMBus, read(_, _))
129         .WillOnce(Return(0x0000))
130         .WillOnce(Return(status_word::TEMPERATURE_FAULT_WARN));
131     psu2.analyze();
132     psu2.analyze();
133     EXPECT_EQ(psu2.isPresent(), true);
134     EXPECT_EQ(psu2.isFaulted(), false);
135     EXPECT_EQ(psu2.hasInputFault(), false);
136     EXPECT_EQ(psu2.hasMFRFault(), false);
137     EXPECT_EQ(psu2.hasVINUVFault(), false);
138 
139     // Ignore fan fault
140     EXPECT_CALL(mockPMBus, read(_, _))
141         .WillOnce(Return(0x0000))
142         .WillOnce(Return(status_word::FAN_FAULT));
143     psu2.analyze();
144     psu2.analyze();
145     EXPECT_EQ(psu2.isPresent(), true);
146     EXPECT_EQ(psu2.isFaulted(), false);
147     EXPECT_EQ(psu2.hasInputFault(), false);
148     EXPECT_EQ(psu2.hasMFRFault(), false);
149     EXPECT_EQ(psu2.hasVINUVFault(), false);
150 
151     // TODO: ReadFailure
152 }
153 
154 TEST_F(PowerSupplyTests, OnOffConfig)
155 {
156     auto bus = sdbusplus::bus::new_default();
157     uint8_t data = 0x15;
158 
159     // Test where PSU is NOT present
160     try
161     {
162         EXPECT_CALL(mockedUtil, getPresence(_, StrEq(PSUInventoryPath)))
163             .Times(1)
164             .WillOnce(Return(false));
165         PowerSupply psu{bus, PSUInventoryPath, 4, "0069"};
166         MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus());
167         // If it is not present, I should not be trying to write to it.
168         EXPECT_CALL(mockPMBus, writeBinary(_, _, _)).Times(0);
169         psu.onOffConfig(data);
170     }
171     catch (...)
172     {
173     }
174 
175     // Test where PSU is present
176     try
177     {
178         EXPECT_CALL(mockedUtil, getPresence(_, StrEq(PSUInventoryPath)))
179             .Times(1)
180             .WillOnce(Return(true)); // present
181         PowerSupply psu{bus, PSUInventoryPath, 5, "006a"};
182         MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus());
183         // TODO: ???should I check the filename?
184         EXPECT_CALL(mockPMBus,
185                     writeBinary(_, ElementsAre(0x15), Type::HwmonDeviceDebug))
186             .Times(1);
187         psu.onOffConfig(data);
188     }
189     catch (...)
190     {
191     }
192 }
193 
194 TEST_F(PowerSupplyTests, ClearFaults)
195 {
196     auto bus = sdbusplus::bus::new_default();
197     EXPECT_CALL(mockedUtil, getPresence(_, StrEq(PSUInventoryPath)))
198         .Times(1)
199         .WillOnce(Return(true)); // present
200     PowerSupply psu{bus, PSUInventoryPath, 13, "0068"};
201     EXPECT_EQ(psu.isPresent(), true);
202     EXPECT_EQ(psu.isFaulted(), false);
203     EXPECT_EQ(psu.hasInputFault(), false);
204     EXPECT_EQ(psu.hasMFRFault(), false);
205     EXPECT_EQ(psu.hasVINUVFault(), false);
206     MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus());
207     EXPECT_CALL(mockPMBus, read(_, _)).Times(1).WillOnce(Return(0xFFFF));
208     psu.analyze();
209     EXPECT_EQ(psu.isPresent(), true);
210     EXPECT_EQ(psu.isFaulted(), true);
211     EXPECT_EQ(psu.hasInputFault(), true);
212     EXPECT_EQ(psu.hasMFRFault(), true);
213     EXPECT_EQ(psu.hasVINUVFault(), true);
214     EXPECT_CALL(mockPMBus, read("in1_input", _))
215         .Times(1)
216         .WillOnce(Return(209000));
217     psu.clearFaults();
218     EXPECT_EQ(psu.isPresent(), true);
219     EXPECT_EQ(psu.isFaulted(), false);
220     EXPECT_EQ(psu.hasInputFault(), false);
221     EXPECT_EQ(psu.hasMFRFault(), false);
222     EXPECT_EQ(psu.hasVINUVFault(), false);
223 }
224 
225 TEST_F(PowerSupplyTests, UpdateInventory)
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, 3, "0068"};
232     psu.updateInventory();
233     // TODO: Checks / Story #921
234 }
235 
236 TEST_F(PowerSupplyTests, IsPresent)
237 {
238     auto bus = sdbusplus::bus::new_default();
239     EXPECT_CALL(mockedUtil, getPresence(_, StrEq(PSUInventoryPath))).Times(1);
240     PowerSupply psu{bus, PSUInventoryPath, 3, "0068"};
241     EXPECT_EQ(psu.isPresent(), false);
242 
243     EXPECT_CALL(mockedUtil, getPresence(_, _))
244         .WillOnce(Return(true)); // present
245     PowerSupply psu2{bus, PSUInventoryPath, 10, "006b"};
246     EXPECT_EQ(psu2.isPresent(), true);
247 }
248 
249 TEST_F(PowerSupplyTests, IsFaulted)
250 {
251     auto bus = sdbusplus::bus::new_default();
252     EXPECT_CALL(mockedUtil, getPresence(_, _))
253         .WillOnce(Return(true)); // present
254     PowerSupply psu{bus, PSUInventoryPath, 11, "006f"};
255     EXPECT_EQ(psu.isFaulted(), false);
256     MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus());
257     EXPECT_CALL(mockPMBus, read(_, _)).Times(1).WillOnce(Return(0xFFFF));
258     psu.analyze();
259     EXPECT_EQ(psu.isFaulted(), true);
260 }
261 
262 TEST_F(PowerSupplyTests, HasInputFault)
263 {
264     auto bus = sdbusplus::bus::new_default();
265     EXPECT_CALL(mockedUtil, getPresence(_, _))
266         .WillOnce(Return(true)); // present
267     PowerSupply psu{bus, PSUInventoryPath, 3, "0068"};
268     MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus());
269     EXPECT_EQ(psu.hasInputFault(), false);
270     EXPECT_CALL(mockPMBus, read(_, _)).Times(1).WillOnce(Return(0x0000));
271     psu.analyze();
272     EXPECT_EQ(psu.hasInputFault(), false);
273     EXPECT_CALL(mockPMBus, read(_, _))
274         .Times(1)
275         .WillOnce(Return(status_word::INPUT_FAULT_WARN));
276     psu.analyze();
277     EXPECT_EQ(psu.hasInputFault(), true);
278     EXPECT_CALL(mockPMBus, read(_, _)).Times(1).WillOnce(Return(0x0000));
279     psu.analyze();
280     EXPECT_EQ(psu.hasInputFault(), false);
281 }
282 
283 TEST_F(PowerSupplyTests, HasMFRFault)
284 {
285     auto bus = sdbusplus::bus::new_default();
286     EXPECT_CALL(mockedUtil, getPresence(_, _))
287         .WillOnce(Return(true)); // present
288     PowerSupply psu{bus, PSUInventoryPath, 3, "0068"};
289     MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus());
290     EXPECT_EQ(psu.hasMFRFault(), false);
291     EXPECT_CALL(mockPMBus, read(_, _)).Times(1).WillOnce(Return(0x0000));
292     psu.analyze();
293     EXPECT_EQ(psu.hasMFRFault(), false);
294     EXPECT_CALL(mockPMBus, read(_, _))
295         .Times(1)
296         .WillOnce(Return(status_word::MFR_SPECIFIC_FAULT));
297     psu.analyze();
298     EXPECT_EQ(psu.hasMFRFault(), true);
299     EXPECT_CALL(mockPMBus, read(_, _)).Times(1).WillOnce(Return(0x0000));
300     psu.analyze();
301     EXPECT_EQ(psu.hasMFRFault(), false);
302 }
303 
304 TEST_F(PowerSupplyTests, HasVINUVFault)
305 {
306     auto bus = sdbusplus::bus::new_default();
307     EXPECT_CALL(mockedUtil, getPresence(_, _))
308         .WillOnce(Return(true)); // present
309     PowerSupply psu{bus, PSUInventoryPath, 3, "0068"};
310     MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus());
311     EXPECT_EQ(psu.hasVINUVFault(), false);
312     EXPECT_CALL(mockPMBus, read(_, _)).Times(1).WillOnce(Return(0x0000));
313     psu.analyze();
314     EXPECT_EQ(psu.hasVINUVFault(), false);
315     EXPECT_CALL(mockPMBus, read(_, _))
316         .Times(1)
317         .WillOnce(Return(status_word::VIN_UV_FAULT));
318     psu.analyze();
319     EXPECT_EQ(psu.hasVINUVFault(), true);
320     EXPECT_CALL(mockPMBus, read(_, _)).Times(1).WillOnce(Return(0x0000));
321     psu.analyze();
322     EXPECT_EQ(psu.hasVINUVFault(), false);
323 }
324