xref: /openbmc/phosphor-power/phosphor-power-sequencer/test/basic_device_tests.cpp (revision 739446763f6f398dac57e82e44b07ea198df4761)
1 /**
2  * Copyright © 2025 IBM Corporation
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "basic_device.hpp"
18 #include "mock_gpio.hpp"
19 #include "mock_services.hpp"
20 #include "rail.hpp"
21 #include "services.hpp"
22 
23 #include <cstdint>
24 #include <map>
25 #include <memory>
26 #include <optional>
27 #include <string>
28 #include <utility>
29 #include <vector>
30 
31 #include <gmock/gmock.h>
32 #include <gtest/gtest.h>
33 
34 using namespace phosphor::power::sequencer;
35 
36 using ::testing::Return;
37 using ::testing::Throw;
38 
39 /**
40  * @class BasicDeviceImpl
41  *
42  * Concrete subclass of the BasicDevice abstract class.
43  *
44  * This subclass is required because BasicDevice does not implement all the pure
45  * virtual methods inherited for PowerSequencerDevice, meaning it cannot be
46  * instantiated.
47  *
48  * This class is not intended to be used outside of this file.  It is
49  * implementation detail for testing the BasicDevice class.
50  */
51 class BasicDeviceImpl : public BasicDevice
52 {
53   public:
54     BasicDeviceImpl() = delete;
55     BasicDeviceImpl(const BasicDeviceImpl&) = delete;
56     BasicDeviceImpl(BasicDeviceImpl&&) = delete;
57     BasicDeviceImpl& operator=(const BasicDeviceImpl&) = delete;
58     BasicDeviceImpl& operator=(BasicDeviceImpl&&) = delete;
59     virtual ~BasicDeviceImpl() = default;
60 
BasicDeviceImpl(const std::string & name,uint8_t bus,uint16_t address,const std::string & powerControlGPIOName,const std::string & powerGoodGPIOName,std::vector<std::unique_ptr<Rail>> rails,Services & services)61     explicit BasicDeviceImpl(
62         const std::string& name, uint8_t bus, uint16_t address,
63         const std::string& powerControlGPIOName,
64         const std::string& powerGoodGPIOName,
65         std::vector<std::unique_ptr<Rail>> rails, Services& services) :
66         BasicDevice(name, bus, address, powerControlGPIOName, powerGoodGPIOName,
67                     std::move(rails), services)
68     {}
69 
70     // Mock pure virtual methods
71     MOCK_METHOD(std::vector<int>, getGPIOValues, (Services & services),
72                 (override));
73     MOCK_METHOD(uint16_t, getStatusWord, (uint8_t page), (override));
74     MOCK_METHOD(uint8_t, getStatusVout, (uint8_t page), (override));
75     MOCK_METHOD(double, getReadVout, (uint8_t page), (override));
76     MOCK_METHOD(double, getVoutUVFaultLimit, (uint8_t page), (override));
77     MOCK_METHOD(std::string, findPgoodFault,
78                 (Services & services, const std::string& powerSupplyError,
79                  (std::map<std::string, std::string> & additionalData)),
80                 (override));
81 };
82 
83 /**
84  * Creates a Rail object that checks for a pgood fault using a GPIO.
85  *
86  * @param name Unique name for the rail
87  * @param gpio GPIO line to read to determine the pgood status of the rail
88  * @return Rail object
89  */
createRail(const std::string & name,unsigned int gpioLine)90 static std::unique_ptr<Rail> createRail(const std::string& name,
91                                         unsigned int gpioLine)
92 {
93     std::optional<std::string> presence{};
94     std::optional<uint8_t> page{};
95     bool isPowerSupplyRail{false};
96     bool checkStatusVout{false};
97     bool compareVoltageToLimit{false};
98     bool activeLow{false};
99     std::optional<PgoodGPIO> gpio{PgoodGPIO{gpioLine, activeLow}};
100     return std::make_unique<Rail>(name, presence, page, isPowerSupplyRail,
101                                   checkStatusVout, compareVoltageToLimit, gpio);
102 }
103 
TEST(BasicDeviceTests,Constructor)104 TEST(BasicDeviceTests, Constructor)
105 {
106     // Test where works: Empty vector of rails
107     {
108         std::string name{"xyz_pseq"};
109         uint8_t bus{3};
110         uint16_t address{0x72};
111         std::string powerControlGPIOName{"power-chassis-control"};
112         std::string powerGoodGPIOName{"power-chassis-good"};
113         std::vector<std::unique_ptr<Rail>> rails{};
114         MockServices services;
115         BasicDeviceImpl device{
116             name,
117             bus,
118             address,
119             powerControlGPIOName,
120             powerGoodGPIOName,
121             std::move(rails),
122             services};
123 
124         EXPECT_EQ(device.getName(), name);
125         EXPECT_EQ(device.getBus(), bus);
126         EXPECT_EQ(device.getAddress(), address);
127         EXPECT_EQ(device.getPowerControlGPIOName(), powerControlGPIOName);
128         EXPECT_EQ(device.getPowerGoodGPIOName(), powerGoodGPIOName);
129         EXPECT_TRUE(device.getRails().empty());
130     }
131 
132     // Test where works: Non-empty vector of rails
133     {
134         std::string name{"abc_pseq"};
135         uint8_t bus{0};
136         uint16_t address{0x23};
137         std::string powerControlGPIOName{"power-chassis-control"};
138         std::string powerGoodGPIOName{"power-chassis-good"};
139         std::vector<std::unique_ptr<Rail>> rails{};
140         rails.emplace_back(createRail("VDD", 5));
141         rails.emplace_back(createRail("VIO", 7));
142         MockServices services;
143         BasicDeviceImpl device{
144             name,
145             bus,
146             address,
147             powerControlGPIOName,
148             powerGoodGPIOName,
149             std::move(rails),
150             services};
151 
152         EXPECT_EQ(device.getName(), name);
153         EXPECT_EQ(device.getBus(), bus);
154         EXPECT_EQ(device.getAddress(), address);
155         EXPECT_EQ(device.getPowerControlGPIOName(), powerControlGPIOName);
156         EXPECT_EQ(device.getPowerGoodGPIOName(), powerGoodGPIOName);
157         EXPECT_EQ(device.getRails().size(), 2);
158         EXPECT_EQ(device.getRails()[0]->getName(), "VDD");
159         EXPECT_EQ(device.getRails()[1]->getName(), "VIO");
160     }
161 }
162 
TEST(BasicDeviceTests,GetName)163 TEST(BasicDeviceTests, GetName)
164 {
165     std::string name{"xyz_pseq"};
166     uint8_t bus{0};
167     uint16_t address{0x23};
168     std::string powerControlGPIOName{"power-chassis-control"};
169     std::string powerGoodGPIOName{"power-chassis-good"};
170     std::vector<std::unique_ptr<Rail>> rails{};
171     MockServices services;
172     BasicDeviceImpl device{
173         name,
174         bus,
175         address,
176         powerControlGPIOName,
177         powerGoodGPIOName,
178         std::move(rails),
179         services};
180 
181     EXPECT_EQ(device.getName(), name);
182 }
183 
TEST(BasicDeviceTests,GetBus)184 TEST(BasicDeviceTests, GetBus)
185 {
186     std::string name{"abc_pseq"};
187     uint8_t bus{1};
188     uint16_t address{0x23};
189     std::string powerControlGPIOName{"power-chassis-control"};
190     std::string powerGoodGPIOName{"power-chassis-good"};
191     std::vector<std::unique_ptr<Rail>> rails{};
192     MockServices services;
193     BasicDeviceImpl device{
194         name,
195         bus,
196         address,
197         powerControlGPIOName,
198         powerGoodGPIOName,
199         std::move(rails),
200         services};
201 
202     EXPECT_EQ(device.getBus(), bus);
203 }
204 
TEST(BasicDeviceTests,GetAddress)205 TEST(BasicDeviceTests, GetAddress)
206 {
207     std::string name{"abc_pseq"};
208     uint8_t bus{1};
209     uint16_t address{0x24};
210     std::string powerControlGPIOName{"power-chassis-control"};
211     std::string powerGoodGPIOName{"power-chassis-good"};
212     std::vector<std::unique_ptr<Rail>> rails{};
213     MockServices services;
214     BasicDeviceImpl device{
215         name,
216         bus,
217         address,
218         powerControlGPIOName,
219         powerGoodGPIOName,
220         std::move(rails),
221         services};
222 
223     EXPECT_EQ(device.getAddress(), address);
224 }
225 
TEST(BasicDeviceTests,GetPowerControlGPIOName)226 TEST(BasicDeviceTests, GetPowerControlGPIOName)
227 {
228     std::string name{"xyz_pseq"};
229     uint8_t bus{0};
230     uint16_t address{0x23};
231     std::string powerControlGPIOName{"power-on"};
232     std::string powerGoodGPIOName{"chassis-pgood"};
233     std::vector<std::unique_ptr<Rail>> rails{};
234     MockServices services;
235     BasicDeviceImpl device{
236         name,
237         bus,
238         address,
239         powerControlGPIOName,
240         powerGoodGPIOName,
241         std::move(rails),
242         services};
243 
244     EXPECT_EQ(device.getPowerControlGPIOName(), powerControlGPIOName);
245 }
246 
TEST(BasicDeviceTests,GetPowerGoodGPIOName)247 TEST(BasicDeviceTests, GetPowerGoodGPIOName)
248 {
249     std::string name{"xyz_pseq"};
250     uint8_t bus{0};
251     uint16_t address{0x23};
252     std::string powerControlGPIOName{"power-on"};
253     std::string powerGoodGPIOName{"chassis-pgood"};
254     std::vector<std::unique_ptr<Rail>> rails{};
255     MockServices services;
256     BasicDeviceImpl device{
257         name,
258         bus,
259         address,
260         powerControlGPIOName,
261         powerGoodGPIOName,
262         std::move(rails),
263         services};
264 
265     EXPECT_EQ(device.getPowerGoodGPIOName(), powerGoodGPIOName);
266 }
267 
TEST(BasicDeviceTests,GetRails)268 TEST(BasicDeviceTests, GetRails)
269 {
270     // Empty vector of rails
271     {
272         std::string name{"xyz_pseq"};
273         uint8_t bus{0};
274         uint16_t address{0x23};
275         std::string powerControlGPIOName{"power-chassis-control"};
276         std::string powerGoodGPIOName{"power-chassis-good"};
277         std::vector<std::unique_ptr<Rail>> rails{};
278         MockServices services;
279         BasicDeviceImpl device{
280             name,
281             bus,
282             address,
283             powerControlGPIOName,
284             powerGoodGPIOName,
285             std::move(rails),
286             services};
287 
288         EXPECT_TRUE(device.getRails().empty());
289     }
290 
291     // Non-empty vector of rails
292     {
293         std::string name{"abc_pseq"};
294         uint8_t bus{0};
295         uint16_t address{0x23};
296         std::string powerControlGPIOName{"power-chassis-control"};
297         std::string powerGoodGPIOName{"power-chassis-good"};
298         std::vector<std::unique_ptr<Rail>> rails{};
299         rails.emplace_back(createRail("VDD", 5));
300         rails.emplace_back(createRail("VIO", 7));
301         rails.emplace_back(createRail("VDDR", 9));
302         MockServices services;
303         BasicDeviceImpl device{
304             name,
305             bus,
306             address,
307             powerControlGPIOName,
308             powerGoodGPIOName,
309             std::move(rails),
310             services};
311 
312         EXPECT_EQ(device.getRails().size(), 3);
313         EXPECT_EQ(device.getRails()[0]->getName(), "VDD");
314         EXPECT_EQ(device.getRails()[1]->getName(), "VIO");
315         EXPECT_EQ(device.getRails()[2]->getName(), "VDDR");
316     }
317 }
318 
TEST(BasicDeviceTests,GetPowerControlGPIO)319 TEST(BasicDeviceTests, GetPowerControlGPIO)
320 {
321     std::string name{"xyz_pseq"};
322     uint8_t bus{0};
323     uint16_t address{0x23};
324     std::string powerControlGPIOName{"power-on"};
325     std::string powerGoodGPIOName{"chassis-pgood"};
326     std::vector<std::unique_ptr<Rail>> rails{};
327     MockServices services;
328     BasicDeviceImpl device{
329         name,
330         bus,
331         address,
332         powerControlGPIOName,
333         powerGoodGPIOName,
334         std::move(rails),
335         services};
336 
337     MockGPIO& gpio = static_cast<MockGPIO&>(device.getPowerControlGPIO());
338     EXPECT_CALL(gpio, setValue(1)).Times(1);
339     device.powerOn();
340 }
341 
TEST(BasicDeviceTests,GetPowerGoodGPIO)342 TEST(BasicDeviceTests, GetPowerGoodGPIO)
343 {
344     std::string name{"xyz_pseq"};
345     uint8_t bus{0};
346     uint16_t address{0x23};
347     std::string powerControlGPIOName{"power-on"};
348     std::string powerGoodGPIOName{"chassis-pgood"};
349     std::vector<std::unique_ptr<Rail>> rails{};
350     MockServices services;
351     BasicDeviceImpl device{
352         name,
353         bus,
354         address,
355         powerControlGPIOName,
356         powerGoodGPIOName,
357         std::move(rails),
358         services};
359 
360     MockGPIO& gpio = static_cast<MockGPIO&>(device.getPowerGoodGPIO());
361     EXPECT_CALL(gpio, getValue()).Times(1).WillOnce(Return(0));
362     EXPECT_FALSE(device.getPowerGood());
363 }
364 
TEST(BasicDeviceTests,PowerOn)365 TEST(BasicDeviceTests, PowerOn)
366 {
367     // Test where works
368     {
369         std::string name{"xyz_pseq"};
370         uint8_t bus{0};
371         uint16_t address{0x23};
372         std::string powerControlGPIOName{"power-on"};
373         std::string powerGoodGPIOName{"chassis-pgood"};
374         std::vector<std::unique_ptr<Rail>> rails{};
375         MockServices services;
376         BasicDeviceImpl device{
377             name,
378             bus,
379             address,
380             powerControlGPIOName,
381             powerGoodGPIOName,
382             std::move(rails),
383             services};
384 
385         MockGPIO& gpio = static_cast<MockGPIO&>(device.getPowerControlGPIO());
386         EXPECT_CALL(gpio, requestWrite(1)).Times(1);
387         EXPECT_CALL(gpio, setValue(1)).Times(1);
388         EXPECT_CALL(gpio, release()).Times(1);
389         device.powerOn();
390     }
391 
392     // Test where fails with exception
393     try
394     {
395         std::string name{"xyz_pseq"};
396         uint8_t bus{0};
397         uint16_t address{0x23};
398         std::string powerControlGPIOName{"power-on"};
399         std::string powerGoodGPIOName{"chassis-pgood"};
400         std::vector<std::unique_ptr<Rail>> rails{};
401         MockServices services;
402         BasicDeviceImpl device{
403             name,
404             bus,
405             address,
406             powerControlGPIOName,
407             powerGoodGPIOName,
408             std::move(rails),
409             services};
410 
411         MockGPIO& gpio = static_cast<MockGPIO&>(device.getPowerControlGPIO());
412         EXPECT_CALL(gpio, requestWrite(1))
413             .Times(1)
414             .WillOnce(Throw(std::runtime_error{"Unable to write GPIO"}));
415         device.powerOn();
416         ADD_FAILURE() << "Should not have reached this line.";
417     }
418     catch (const std::exception& e)
419     {
420         EXPECT_STREQ(e.what(), "Unable to write GPIO");
421     }
422 }
423 
TEST(BasicDeviceTests,PowerOff)424 TEST(BasicDeviceTests, PowerOff)
425 {
426     // Test where works
427     {
428         std::string name{"xyz_pseq"};
429         uint8_t bus{0};
430         uint16_t address{0x23};
431         std::string powerControlGPIOName{"power-on"};
432         std::string powerGoodGPIOName{"chassis-pgood"};
433         std::vector<std::unique_ptr<Rail>> rails{};
434         MockServices services;
435         BasicDeviceImpl device{
436             name,
437             bus,
438             address,
439             powerControlGPIOName,
440             powerGoodGPIOName,
441             std::move(rails),
442             services};
443 
444         MockGPIO& gpio = static_cast<MockGPIO&>(device.getPowerControlGPIO());
445         EXPECT_CALL(gpio, requestWrite(0)).Times(1);
446         EXPECT_CALL(gpio, setValue(0)).Times(1);
447         EXPECT_CALL(gpio, release()).Times(1);
448         device.powerOff();
449     }
450 
451     // Test where fails with exception
452     try
453     {
454         std::string name{"xyz_pseq"};
455         uint8_t bus{0};
456         uint16_t address{0x23};
457         std::string powerControlGPIOName{"power-on"};
458         std::string powerGoodGPIOName{"chassis-pgood"};
459         std::vector<std::unique_ptr<Rail>> rails{};
460         MockServices services;
461         BasicDeviceImpl device{
462             name,
463             bus,
464             address,
465             powerControlGPIOName,
466             powerGoodGPIOName,
467             std::move(rails),
468             services};
469 
470         MockGPIO& gpio = static_cast<MockGPIO&>(device.getPowerControlGPIO());
471         EXPECT_CALL(gpio, requestWrite(0)).Times(1);
472         EXPECT_CALL(gpio, setValue(0))
473             .Times(1)
474             .WillOnce(Throw(std::runtime_error{"Unable to write GPIO"}));
475         device.powerOff();
476         ADD_FAILURE() << "Should not have reached this line.";
477     }
478     catch (const std::exception& e)
479     {
480         EXPECT_STREQ(e.what(), "Unable to write GPIO");
481     }
482 }
483 
TEST(BasicDeviceTests,GetPowerGood)484 TEST(BasicDeviceTests, GetPowerGood)
485 {
486     // Test where works: Value is false
487     {
488         std::string name{"xyz_pseq"};
489         uint8_t bus{0};
490         uint16_t address{0x23};
491         std::string powerControlGPIOName{"power-on"};
492         std::string powerGoodGPIOName{"chassis-pgood"};
493         std::vector<std::unique_ptr<Rail>> rails{};
494         MockServices services;
495         BasicDeviceImpl device{
496             name,
497             bus,
498             address,
499             powerControlGPIOName,
500             powerGoodGPIOName,
501             std::move(rails),
502             services};
503 
504         MockGPIO& gpio = static_cast<MockGPIO&>(device.getPowerGoodGPIO());
505         EXPECT_CALL(gpio, getValue()).Times(1).WillOnce(Return(0));
506         EXPECT_FALSE(device.getPowerGood());
507     }
508 
509     // Test where works: Value is true
510     {
511         std::string name{"xyz_pseq"};
512         uint8_t bus{0};
513         uint16_t address{0x23};
514         std::string powerControlGPIOName{"power-on"};
515         std::string powerGoodGPIOName{"chassis-pgood"};
516         std::vector<std::unique_ptr<Rail>> rails{};
517         MockServices services;
518         BasicDeviceImpl device{
519             name,
520             bus,
521             address,
522             powerControlGPIOName,
523             powerGoodGPIOName,
524             std::move(rails),
525             services};
526 
527         MockGPIO& gpio = static_cast<MockGPIO&>(device.getPowerGoodGPIO());
528         EXPECT_CALL(gpio, getValue()).Times(1).WillOnce(Return(1));
529         EXPECT_TRUE(device.getPowerGood());
530     }
531 
532     // Test where fails with exception
533     try
534     {
535         std::string name{"xyz_pseq"};
536         uint8_t bus{0};
537         uint16_t address{0x23};
538         std::string powerControlGPIOName{"power-on"};
539         std::string powerGoodGPIOName{"chassis-pgood"};
540         std::vector<std::unique_ptr<Rail>> rails{};
541         MockServices services;
542         BasicDeviceImpl device{
543             name,
544             bus,
545             address,
546             powerControlGPIOName,
547             powerGoodGPIOName,
548             std::move(rails),
549             services};
550 
551         MockGPIO& gpio = static_cast<MockGPIO&>(device.getPowerGoodGPIO());
552         EXPECT_CALL(gpio, getValue())
553             .Times(1)
554             .WillOnce(Throw(std::runtime_error{"Unable to read GPIO"}));
555         device.getPowerGood();
556         ADD_FAILURE() << "Should not have reached this line.";
557     }
558     catch (const std::exception& e)
559     {
560         EXPECT_STREQ(e.what(), "Unable to read GPIO");
561     }
562 }
563