xref: /openbmc/phosphor-power/phosphor-power-sequencer/test/standard_device_tests.cpp (revision fdaa950b078e162b99dd1358f66b942a26b3bd19)
1 /**
2  * Copyright © 2024 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 "mock_gpio.hpp"
18 #include "mock_services.hpp"
19 #include "rail.hpp"
20 #include "services.hpp"
21 #include "standard_device.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 StandardDeviceImpl
41  *
42  * Concrete subclass of the StandardDevice abstract class.
43  *
44  * This subclass is required for two reasons:
45  * - StandardDevice has some pure virtual methods so it cannot be instantiated.
46  * - The pure virtual methods provide the PMBus and GPIO information.  Mocking
47  *   these makes it possible to test the pgood fault detection algorithm.
48  *
49  * This class is not intended to be used outside of this file.  It is
50  * implementation detail for testing the the StandardDevice class.
51  */
52 class StandardDeviceImpl : public StandardDevice
53 {
54   public:
55     // Specify which compiler-generated methods we want
56     StandardDeviceImpl() = delete;
57     StandardDeviceImpl(const StandardDeviceImpl&) = delete;
58     StandardDeviceImpl(StandardDeviceImpl&&) = delete;
59     StandardDeviceImpl& operator=(const StandardDeviceImpl&) = delete;
60     StandardDeviceImpl& operator=(StandardDeviceImpl&&) = delete;
61     virtual ~StandardDeviceImpl() = default;
62 
63     // Constructor just calls StandardDevice constructor
StandardDeviceImpl(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)64     explicit StandardDeviceImpl(
65         const std::string& name, uint8_t bus, uint16_t address,
66         const std::string& powerControlGPIOName,
67         const std::string& powerGoodGPIOName,
68         std::vector<std::unique_ptr<Rail>> rails, Services& services) :
69         StandardDevice(name, bus, address, powerControlGPIOName,
70                        powerGoodGPIOName, std::move(rails), services)
71     {}
72 
73     // Mock pure virtual methods
74     MOCK_METHOD(std::vector<int>, getGPIOValues, (Services & services),
75                 (override));
76     MOCK_METHOD(uint16_t, getStatusWord, (uint8_t page), (override));
77     MOCK_METHOD(uint8_t, getStatusVout, (uint8_t page), (override));
78     MOCK_METHOD(double, getReadVout, (uint8_t page), (override));
79     MOCK_METHOD(double, getVoutUVFaultLimit, (uint8_t page), (override));
80 
81     // Override empty implementation with mock so we can verify it is called
82     MOCK_METHOD(void, prepareForPgoodFaultDetection, (Services & services),
83                 (override));
84 };
85 
86 /**
87  * Creates a Rail object that checks for a pgood fault using STATUS_VOUT.
88  *
89  * @param name Unique name for the rail
90  * @param isPowerSupplyRail Specifies whether the rail is produced by a
91                             power supply
92  * @param pageNum PMBus PAGE number of the rail
93  * @return Rail object
94  */
createRailStatusVout(const std::string & name,bool isPowerSupplyRail,uint8_t pageNum)95 std::unique_ptr<Rail> createRailStatusVout(
96     const std::string& name, bool isPowerSupplyRail, uint8_t pageNum)
97 {
98     std::optional<std::string> presence{};
99     std::optional<uint8_t> page{pageNum};
100     bool checkStatusVout{true};
101     bool compareVoltageToLimit{false};
102     std::optional<PgoodGPIO> gpio{};
103     return std::make_unique<Rail>(name, presence, page, isPowerSupplyRail,
104                                   checkStatusVout, compareVoltageToLimit, gpio);
105 }
106 
107 /**
108  * Creates a Rail object that checks for a pgood fault using a GPIO.
109  *
110  * @param name Unique name for the rail
111  * @param isPowerSupplyRail Specifies whether the rail is produced by a
112                             power supply
113  * @param gpio GPIO line to read to determine the pgood status of the rail
114  * @return Rail object
115  */
createRailGPIO(const std::string & name,bool isPowerSupplyRail,unsigned int gpioLine)116 std::unique_ptr<Rail> createRailGPIO(
117     const std::string& name, bool isPowerSupplyRail, unsigned int gpioLine)
118 {
119     std::optional<std::string> presence{};
120     std::optional<uint8_t> page{};
121     bool checkStatusVout{false};
122     bool compareVoltageToLimit{false};
123     bool activeLow{false};
124     std::optional<PgoodGPIO> gpio{PgoodGPIO{gpioLine, activeLow}};
125     return std::make_unique<Rail>(name, presence, page, isPowerSupplyRail,
126                                   checkStatusVout, compareVoltageToLimit, gpio);
127 }
128 
129 /**
130  * Creates a Rail object that checks for a pgood fault using output voltage.
131  *
132  * @param name Unique name for the rail
133  * @param isPowerSupplyRail Specifies whether the rail is produced by a
134                             power supply
135  * @param pageNum PMBus PAGE number of the rail
136  * @return Rail object
137  */
createRailOutputVoltage(const std::string & name,bool isPowerSupplyRail,uint8_t pageNum)138 std::unique_ptr<Rail> createRailOutputVoltage(
139     const std::string& name, bool isPowerSupplyRail, uint8_t pageNum)
140 {
141     std::optional<std::string> presence{};
142     std::optional<uint8_t> page{pageNum};
143     bool checkStatusVout{false};
144     bool compareVoltageToLimit{true};
145     std::optional<PgoodGPIO> gpio{};
146     return std::make_unique<Rail>(name, presence, page, isPowerSupplyRail,
147                                   checkStatusVout, compareVoltageToLimit, gpio);
148 }
149 
TEST(StandardDeviceTests,Constructor)150 TEST(StandardDeviceTests, Constructor)
151 {
152     // Test where works: Empty vector of rails
153     {
154         std::string name{"xyz_pseq"};
155         uint8_t bus{3};
156         uint16_t address{0x72};
157         std::string powerControlGPIOName{"power-chassis-control"};
158         std::string powerGoodGPIOName{"power-chassis-good"};
159         std::vector<std::unique_ptr<Rail>> rails{};
160         MockServices services;
161         StandardDeviceImpl device{
162             name,
163             bus,
164             address,
165             powerControlGPIOName,
166             powerGoodGPIOName,
167             std::move(rails),
168             services};
169 
170         EXPECT_EQ(device.getName(), name);
171         EXPECT_EQ(device.getBus(), bus);
172         EXPECT_EQ(device.getAddress(), address);
173         EXPECT_EQ(device.getPowerControlGPIOName(), powerControlGPIOName);
174         EXPECT_EQ(device.getPowerGoodGPIOName(), powerGoodGPIOName);
175         EXPECT_TRUE(device.getRails().empty());
176     }
177 
178     // Test where works: Non-empty vector of rails
179     {
180         std::string name{"abc_pseq"};
181         uint8_t bus{0};
182         uint16_t address{0x23};
183         std::string powerControlGPIOName{"power-chassis-control"};
184         std::string powerGoodGPIOName{"power-chassis-good"};
185         std::vector<std::unique_ptr<Rail>> rails{};
186         rails.emplace_back(createRailGPIO("PSU", true, 3));
187         rails.emplace_back(createRailOutputVoltage("VDD", false, 5));
188         rails.emplace_back(createRailStatusVout("VIO", false, 7));
189         MockServices services;
190         StandardDeviceImpl device{
191             name,
192             bus,
193             address,
194             powerControlGPIOName,
195             powerGoodGPIOName,
196             std::move(rails),
197             services};
198 
199         EXPECT_EQ(device.getName(), name);
200         EXPECT_EQ(device.getBus(), bus);
201         EXPECT_EQ(device.getAddress(), address);
202         EXPECT_EQ(device.getPowerControlGPIOName(), powerControlGPIOName);
203         EXPECT_EQ(device.getPowerGoodGPIOName(), powerGoodGPIOName);
204         EXPECT_EQ(device.getRails().size(), 3);
205         EXPECT_EQ(device.getRails()[0]->getName(), "PSU");
206         EXPECT_EQ(device.getRails()[1]->getName(), "VDD");
207         EXPECT_EQ(device.getRails()[2]->getName(), "VIO");
208     }
209 }
210 
TEST(StandardDeviceTests,GetName)211 TEST(StandardDeviceTests, GetName)
212 {
213     std::string name{"xyz_pseq"};
214     uint8_t bus{0};
215     uint16_t address{0x23};
216     std::string powerControlGPIOName{"power-chassis-control"};
217     std::string powerGoodGPIOName{"power-chassis-good"};
218     std::vector<std::unique_ptr<Rail>> rails{};
219     MockServices services;
220     StandardDeviceImpl device{
221         name,
222         bus,
223         address,
224         powerControlGPIOName,
225         powerGoodGPIOName,
226         std::move(rails),
227         services};
228 
229     EXPECT_EQ(device.getName(), name);
230 }
231 
TEST(StandardDeviceTests,GetBus)232 TEST(StandardDeviceTests, GetBus)
233 {
234     std::string name{"abc_pseq"};
235     uint8_t bus{1};
236     uint16_t address{0x23};
237     std::string powerControlGPIOName{"power-chassis-control"};
238     std::string powerGoodGPIOName{"power-chassis-good"};
239     std::vector<std::unique_ptr<Rail>> rails{};
240     MockServices services;
241     StandardDeviceImpl device{
242         name,
243         bus,
244         address,
245         powerControlGPIOName,
246         powerGoodGPIOName,
247         std::move(rails),
248         services};
249 
250     EXPECT_EQ(device.getBus(), bus);
251 }
252 
TEST(StandardDeviceTests,GetAddress)253 TEST(StandardDeviceTests, GetAddress)
254 {
255     std::string name{"abc_pseq"};
256     uint8_t bus{1};
257     uint16_t address{0x24};
258     std::string powerControlGPIOName{"power-chassis-control"};
259     std::string powerGoodGPIOName{"power-chassis-good"};
260     std::vector<std::unique_ptr<Rail>> rails{};
261     MockServices services;
262     StandardDeviceImpl device{
263         name,
264         bus,
265         address,
266         powerControlGPIOName,
267         powerGoodGPIOName,
268         std::move(rails),
269         services};
270 
271     EXPECT_EQ(device.getAddress(), address);
272 }
273 
TEST(StandardDeviceTests,GetPowerControlGPIOName)274 TEST(StandardDeviceTests, GetPowerControlGPIOName)
275 {
276     std::string name{"xyz_pseq"};
277     uint8_t bus{0};
278     uint16_t address{0x23};
279     std::string powerControlGPIOName{"power-on"};
280     std::string powerGoodGPIOName{"chassis-pgood"};
281     std::vector<std::unique_ptr<Rail>> rails{};
282     MockServices services;
283     StandardDeviceImpl device{
284         name,
285         bus,
286         address,
287         powerControlGPIOName,
288         powerGoodGPIOName,
289         std::move(rails),
290         services};
291 
292     EXPECT_EQ(device.getPowerControlGPIOName(), powerControlGPIOName);
293 }
294 
TEST(StandardDeviceTests,GetPowerGoodGPIOName)295 TEST(StandardDeviceTests, GetPowerGoodGPIOName)
296 {
297     std::string name{"xyz_pseq"};
298     uint8_t bus{0};
299     uint16_t address{0x23};
300     std::string powerControlGPIOName{"power-on"};
301     std::string powerGoodGPIOName{"chassis-pgood"};
302     std::vector<std::unique_ptr<Rail>> rails{};
303     MockServices services;
304     StandardDeviceImpl device{
305         name,
306         bus,
307         address,
308         powerControlGPIOName,
309         powerGoodGPIOName,
310         std::move(rails),
311         services};
312 
313     EXPECT_EQ(device.getPowerGoodGPIOName(), powerGoodGPIOName);
314 }
315 
TEST(StandardDeviceTests,GetRails)316 TEST(StandardDeviceTests, GetRails)
317 {
318     // Empty vector of rails
319     {
320         std::string name{"xyz_pseq"};
321         uint8_t bus{0};
322         uint16_t address{0x23};
323         std::string powerControlGPIOName{"power-chassis-control"};
324         std::string powerGoodGPIOName{"power-chassis-good"};
325         std::vector<std::unique_ptr<Rail>> rails{};
326         MockServices services;
327         StandardDeviceImpl device{
328             name,
329             bus,
330             address,
331             powerControlGPIOName,
332             powerGoodGPIOName,
333             std::move(rails),
334             services};
335 
336         EXPECT_TRUE(device.getRails().empty());
337     }
338 
339     // Non-empty vector of rails
340     {
341         std::string name{"abc_pseq"};
342         uint8_t bus{0};
343         uint16_t address{0x23};
344         std::string powerControlGPIOName{"power-chassis-control"};
345         std::string powerGoodGPIOName{"power-chassis-good"};
346         std::vector<std::unique_ptr<Rail>> rails{};
347         rails.emplace_back(createRailGPIO("PSU", true, 3));
348         rails.emplace_back(createRailOutputVoltage("VDD", false, 5));
349         rails.emplace_back(createRailStatusVout("VIO", false, 7));
350         MockServices services;
351         StandardDeviceImpl device{
352             name,
353             bus,
354             address,
355             powerControlGPIOName,
356             powerGoodGPIOName,
357             std::move(rails),
358             services};
359 
360         EXPECT_EQ(device.getRails().size(), 3);
361         EXPECT_EQ(device.getRails()[0]->getName(), "PSU");
362         EXPECT_EQ(device.getRails()[1]->getName(), "VDD");
363         EXPECT_EQ(device.getRails()[2]->getName(), "VIO");
364     }
365 }
366 
TEST(StandardDeviceTests,GetPowerControlGPIO)367 TEST(StandardDeviceTests, GetPowerControlGPIO)
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     StandardDeviceImpl 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, setValue(1)).Times(1);
387     device.powerOn();
388 }
389 
TEST(StandardDeviceTests,GetPowerGoodGPIO)390 TEST(StandardDeviceTests, GetPowerGoodGPIO)
391 {
392     std::string name{"xyz_pseq"};
393     uint8_t bus{0};
394     uint16_t address{0x23};
395     std::string powerControlGPIOName{"power-on"};
396     std::string powerGoodGPIOName{"chassis-pgood"};
397     std::vector<std::unique_ptr<Rail>> rails{};
398     MockServices services;
399     StandardDeviceImpl device{
400         name,
401         bus,
402         address,
403         powerControlGPIOName,
404         powerGoodGPIOName,
405         std::move(rails),
406         services};
407 
408     MockGPIO& gpio = static_cast<MockGPIO&>(device.getPowerGoodGPIO());
409     EXPECT_CALL(gpio, getValue()).Times(1).WillOnce(Return(0));
410     EXPECT_FALSE(device.getPowerGood());
411 }
412 
TEST(StandardDeviceTests,PowerOn)413 TEST(StandardDeviceTests, PowerOn)
414 {
415     // Test where works
416     {
417         std::string name{"xyz_pseq"};
418         uint8_t bus{0};
419         uint16_t address{0x23};
420         std::string powerControlGPIOName{"power-on"};
421         std::string powerGoodGPIOName{"chassis-pgood"};
422         std::vector<std::unique_ptr<Rail>> rails{};
423         MockServices services;
424         StandardDeviceImpl device{
425             name,
426             bus,
427             address,
428             powerControlGPIOName,
429             powerGoodGPIOName,
430             std::move(rails),
431             services};
432 
433         MockGPIO& gpio = static_cast<MockGPIO&>(device.getPowerControlGPIO());
434         EXPECT_CALL(gpio, requestWrite(1)).Times(1);
435         EXPECT_CALL(gpio, setValue(1)).Times(1);
436         EXPECT_CALL(gpio, release()).Times(1);
437         device.powerOn();
438     }
439 
440     // Test where fails with exception
441     try
442     {
443         std::string name{"xyz_pseq"};
444         uint8_t bus{0};
445         uint16_t address{0x23};
446         std::string powerControlGPIOName{"power-on"};
447         std::string powerGoodGPIOName{"chassis-pgood"};
448         std::vector<std::unique_ptr<Rail>> rails{};
449         MockServices services;
450         StandardDeviceImpl device{
451             name,
452             bus,
453             address,
454             powerControlGPIOName,
455             powerGoodGPIOName,
456             std::move(rails),
457             services};
458 
459         MockGPIO& gpio = static_cast<MockGPIO&>(device.getPowerControlGPIO());
460         EXPECT_CALL(gpio, requestWrite(1))
461             .Times(1)
462             .WillOnce(Throw(std::runtime_error{"Unable to write GPIO"}));
463         device.powerOn();
464         ADD_FAILURE() << "Should not have reached this line.";
465     }
466     catch (const std::exception& e)
467     {
468         EXPECT_STREQ(e.what(), "Unable to write GPIO");
469     }
470 }
471 
TEST(StandardDeviceTests,PowerOff)472 TEST(StandardDeviceTests, PowerOff)
473 {
474     // Test where works
475     {
476         std::string name{"xyz_pseq"};
477         uint8_t bus{0};
478         uint16_t address{0x23};
479         std::string powerControlGPIOName{"power-on"};
480         std::string powerGoodGPIOName{"chassis-pgood"};
481         std::vector<std::unique_ptr<Rail>> rails{};
482         MockServices services;
483         StandardDeviceImpl device{
484             name,
485             bus,
486             address,
487             powerControlGPIOName,
488             powerGoodGPIOName,
489             std::move(rails),
490             services};
491 
492         MockGPIO& gpio = static_cast<MockGPIO&>(device.getPowerControlGPIO());
493         EXPECT_CALL(gpio, requestWrite(0)).Times(1);
494         EXPECT_CALL(gpio, setValue(0)).Times(1);
495         EXPECT_CALL(gpio, release()).Times(1);
496         device.powerOff();
497     }
498 
499     // Test where fails with exception
500     try
501     {
502         std::string name{"xyz_pseq"};
503         uint8_t bus{0};
504         uint16_t address{0x23};
505         std::string powerControlGPIOName{"power-on"};
506         std::string powerGoodGPIOName{"chassis-pgood"};
507         std::vector<std::unique_ptr<Rail>> rails{};
508         MockServices services;
509         StandardDeviceImpl device{
510             name,
511             bus,
512             address,
513             powerControlGPIOName,
514             powerGoodGPIOName,
515             std::move(rails),
516             services};
517 
518         MockGPIO& gpio = static_cast<MockGPIO&>(device.getPowerControlGPIO());
519         EXPECT_CALL(gpio, requestWrite(0)).Times(1);
520         EXPECT_CALL(gpio, setValue(0))
521             .Times(1)
522             .WillOnce(Throw(std::runtime_error{"Unable to write GPIO"}));
523         device.powerOff();
524         ADD_FAILURE() << "Should not have reached this line.";
525     }
526     catch (const std::exception& e)
527     {
528         EXPECT_STREQ(e.what(), "Unable to write GPIO");
529     }
530 }
531 
TEST(StandardDeviceTests,GetPowerGood)532 TEST(StandardDeviceTests, GetPowerGood)
533 {
534     // Test where works: Value is false
535     {
536         std::string name{"xyz_pseq"};
537         uint8_t bus{0};
538         uint16_t address{0x23};
539         std::string powerControlGPIOName{"power-on"};
540         std::string powerGoodGPIOName{"chassis-pgood"};
541         std::vector<std::unique_ptr<Rail>> rails{};
542         MockServices services;
543         StandardDeviceImpl device{
544             name,
545             bus,
546             address,
547             powerControlGPIOName,
548             powerGoodGPIOName,
549             std::move(rails),
550             services};
551 
552         MockGPIO& gpio = static_cast<MockGPIO&>(device.getPowerGoodGPIO());
553         EXPECT_CALL(gpio, getValue()).Times(1).WillOnce(Return(0));
554         EXPECT_FALSE(device.getPowerGood());
555     }
556 
557     // Test where works: Value is true
558     {
559         std::string name{"xyz_pseq"};
560         uint8_t bus{0};
561         uint16_t address{0x23};
562         std::string powerControlGPIOName{"power-on"};
563         std::string powerGoodGPIOName{"chassis-pgood"};
564         std::vector<std::unique_ptr<Rail>> rails{};
565         MockServices services;
566         StandardDeviceImpl device{
567             name,
568             bus,
569             address,
570             powerControlGPIOName,
571             powerGoodGPIOName,
572             std::move(rails),
573             services};
574 
575         MockGPIO& gpio = static_cast<MockGPIO&>(device.getPowerGoodGPIO());
576         EXPECT_CALL(gpio, getValue()).Times(1).WillOnce(Return(1));
577         EXPECT_TRUE(device.getPowerGood());
578     }
579 
580     // Test where fails with exception
581     try
582     {
583         std::string name{"xyz_pseq"};
584         uint8_t bus{0};
585         uint16_t address{0x23};
586         std::string powerControlGPIOName{"power-on"};
587         std::string powerGoodGPIOName{"chassis-pgood"};
588         std::vector<std::unique_ptr<Rail>> rails{};
589         MockServices services;
590         StandardDeviceImpl device{
591             name,
592             bus,
593             address,
594             powerControlGPIOName,
595             powerGoodGPIOName,
596             std::move(rails),
597             services};
598 
599         MockGPIO& gpio = static_cast<MockGPIO&>(device.getPowerGoodGPIO());
600         EXPECT_CALL(gpio, getValue())
601             .Times(1)
602             .WillOnce(Throw(std::runtime_error{"Unable to read GPIO"}));
603         device.getPowerGood();
604         ADD_FAILURE() << "Should not have reached this line.";
605     }
606     catch (const std::exception& e)
607     {
608         EXPECT_STREQ(e.what(), "Unable to read GPIO");
609     }
610 }
611 
TEST(StandardDeviceTests,FindPgoodFault)612 TEST(StandardDeviceTests, FindPgoodFault)
613 {
614     // No rail has a pgood fault
615     {
616         std::string name{"abc_pseq"};
617         uint8_t bus{0};
618         uint16_t address{0x23};
619         std::string powerControlGPIOName{"power-chassis-control"};
620         std::string powerGoodGPIOName{"power-chassis-good"};
621         std::vector<std::unique_ptr<Rail>> rails{};
622         rails.emplace_back(createRailGPIO("PSU", true, 2));
623         rails.emplace_back(createRailOutputVoltage("VDD", false, 5));
624         rails.emplace_back(createRailStatusVout("VIO", false, 7));
625         MockServices services;
626         StandardDeviceImpl device{
627             name,
628             bus,
629             address,
630             powerControlGPIOName,
631             powerGoodGPIOName,
632             std::move(rails),
633             services};
634 
635         EXPECT_CALL(device, prepareForPgoodFaultDetection).Times(1);
636         std::vector<int> gpioValues{1, 1, 1};
637         EXPECT_CALL(device, getGPIOValues)
638             .Times(1)
639             .WillOnce(Return(gpioValues));
640         EXPECT_CALL(device, getReadVout(5)).Times(1).WillOnce(Return(1.2));
641         EXPECT_CALL(device, getVoutUVFaultLimit(5))
642             .Times(1)
643             .WillOnce(Return(1.1));
644         EXPECT_CALL(device, getStatusVout(7)).Times(1).WillOnce(Return(0x00));
645 
646         std::string powerSupplyError{};
647         std::map<std::string, std::string> additionalData{};
648         std::string error =
649             device.findPgoodFault(services, powerSupplyError, additionalData);
650         EXPECT_TRUE(error.empty());
651         EXPECT_EQ(additionalData.size(), 0);
652     }
653 
654     // First rail has a pgood fault detected via GPIO
655     // Is a PSU rail: No PSU error specified
656     {
657         std::string name{"abc_pseq"};
658         uint8_t bus{0};
659         uint16_t address{0x23};
660         std::string powerControlGPIOName{"power-chassis-control"};
661         std::string powerGoodGPIOName{"power-chassis-good"};
662         std::vector<std::unique_ptr<Rail>> rails{};
663         rails.emplace_back(createRailGPIO("PSU", true, 2));
664         rails.emplace_back(createRailOutputVoltage("VDD", false, 5));
665         rails.emplace_back(createRailStatusVout("VIO", false, 7));
666         MockServices services;
667         StandardDeviceImpl device{
668             name,
669             bus,
670             address,
671             powerControlGPIOName,
672             powerGoodGPIOName,
673             std::move(rails),
674             services};
675 
676         EXPECT_CALL(device, prepareForPgoodFaultDetection).Times(1);
677         std::vector<int> gpioValues{1, 1, 0};
678         EXPECT_CALL(device, getGPIOValues)
679             .Times(1)
680             .WillOnce(Return(gpioValues));
681         EXPECT_CALL(device, getReadVout(5)).Times(0);
682         EXPECT_CALL(device, getVoutUVFaultLimit(5)).Times(0);
683         EXPECT_CALL(device, getStatusVout(7)).Times(1).WillOnce(Return(0x00));
684 
685         EXPECT_CALL(services,
686                     logInfoMsg("Device abc_pseq GPIO values: [1, 1, 0]"))
687             .Times(1);
688         EXPECT_CALL(
689             services,
690             logErrorMsg(
691                 "Pgood fault found in rail monitored by device abc_pseq"))
692             .Times(1);
693         EXPECT_CALL(services, logErrorMsg("Pgood fault detected in rail PSU"))
694             .Times(1);
695         EXPECT_CALL(
696             services,
697             logErrorMsg(
698                 "Rail PSU pgood GPIO line offset 2 has inactive value 0"))
699             .Times(1);
700 
701         std::string powerSupplyError{};
702         std::map<std::string, std::string> additionalData{};
703         std::string error =
704             device.findPgoodFault(services, powerSupplyError, additionalData);
705         EXPECT_EQ(error,
706                   "xyz.openbmc_project.Power.Error.PowerSequencerVoltageFault");
707         EXPECT_EQ(additionalData.size(), 5);
708         EXPECT_EQ(additionalData["DEVICE_NAME"], "abc_pseq");
709         EXPECT_EQ(additionalData["GPIO_VALUES"], "[1, 1, 0]");
710         EXPECT_EQ(additionalData["RAIL_NAME"], "PSU");
711         EXPECT_EQ(additionalData["GPIO_LINE"], "2");
712         EXPECT_EQ(additionalData["GPIO_VALUE"], "0");
713     }
714 
715     // First rail has a pgood fault detected via GPIO
716     // Is a PSU rail: PSU error specified
717     {
718         std::string name{"abc_pseq"};
719         uint8_t bus{0};
720         uint16_t address{0x23};
721         std::string powerControlGPIOName{"power-chassis-control"};
722         std::string powerGoodGPIOName{"power-chassis-good"};
723         std::vector<std::unique_ptr<Rail>> rails{};
724         rails.emplace_back(createRailGPIO("PSU", true, 2));
725         rails.emplace_back(createRailOutputVoltage("VDD", false, 5));
726         rails.emplace_back(createRailStatusVout("VIO", false, 7));
727         MockServices services;
728         StandardDeviceImpl device{
729             name,
730             bus,
731             address,
732             powerControlGPIOName,
733             powerGoodGPIOName,
734             std::move(rails),
735             services};
736 
737         EXPECT_CALL(device, prepareForPgoodFaultDetection).Times(1);
738         std::vector<int> gpioValues{1, 1, 0};
739         EXPECT_CALL(device, getGPIOValues)
740             .Times(1)
741             .WillOnce(Return(gpioValues));
742         EXPECT_CALL(device, getReadVout(5)).Times(0);
743         EXPECT_CALL(device, getVoutUVFaultLimit(5)).Times(0);
744         EXPECT_CALL(device, getStatusVout(7)).Times(1).WillOnce(Return(0x00));
745 
746         EXPECT_CALL(services,
747                     logInfoMsg("Device abc_pseq GPIO values: [1, 1, 0]"))
748             .Times(1);
749         EXPECT_CALL(
750             services,
751             logErrorMsg(
752                 "Pgood fault found in rail monitored by device abc_pseq"))
753             .Times(1);
754         EXPECT_CALL(services, logErrorMsg("Pgood fault detected in rail PSU"))
755             .Times(1);
756         EXPECT_CALL(
757             services,
758             logErrorMsg(
759                 "Rail PSU pgood GPIO line offset 2 has inactive value 0"))
760             .Times(1);
761 
762         std::string powerSupplyError{"Undervoltage fault: PSU1"};
763         std::map<std::string, std::string> additionalData{};
764         std::string error =
765             device.findPgoodFault(services, powerSupplyError, additionalData);
766         EXPECT_EQ(error, powerSupplyError);
767         EXPECT_EQ(additionalData.size(), 5);
768         EXPECT_EQ(additionalData["DEVICE_NAME"], "abc_pseq");
769         EXPECT_EQ(additionalData["GPIO_VALUES"], "[1, 1, 0]");
770         EXPECT_EQ(additionalData["RAIL_NAME"], "PSU");
771         EXPECT_EQ(additionalData["GPIO_LINE"], "2");
772         EXPECT_EQ(additionalData["GPIO_VALUE"], "0");
773     }
774 
775     // Second rail has a pgood fault detected via output voltage
776     // Not a PSU rail: PSU error specified
777     {
778         std::string name{"abc_pseq"};
779         uint8_t bus{0};
780         uint16_t address{0x23};
781         std::string powerControlGPIOName{"power-chassis-control"};
782         std::string powerGoodGPIOName{"power-chassis-good"};
783         std::vector<std::unique_ptr<Rail>> rails{};
784         rails.emplace_back(createRailGPIO("PSU", true, 2));
785         rails.emplace_back(createRailOutputVoltage("VDD", false, 5));
786         rails.emplace_back(createRailStatusVout("VIO", false, 7));
787         MockServices services;
788         StandardDeviceImpl device{
789             name,
790             bus,
791             address,
792             powerControlGPIOName,
793             powerGoodGPIOName,
794             std::move(rails),
795             services};
796 
797         EXPECT_CALL(device, prepareForPgoodFaultDetection).Times(1);
798         std::vector<int> gpioValues{1, 1, 1};
799         EXPECT_CALL(device, getGPIOValues)
800             .Times(1)
801             .WillOnce(Return(gpioValues));
802         EXPECT_CALL(device, getReadVout(5)).Times(1).WillOnce(Return(1.1));
803         EXPECT_CALL(device, getVoutUVFaultLimit(5))
804             .Times(1)
805             .WillOnce(Return(1.2));
806         EXPECT_CALL(device, getStatusVout(7)).Times(1).WillOnce(Return(0x00));
807         EXPECT_CALL(device, getStatusWord(5)).Times(1).WillOnce(Return(0xbeef));
808 
809         EXPECT_CALL(services,
810                     logInfoMsg("Device abc_pseq GPIO values: [1, 1, 1]"))
811             .Times(1);
812         EXPECT_CALL(
813             services,
814             logErrorMsg(
815                 "Pgood fault found in rail monitored by device abc_pseq"))
816             .Times(1);
817         EXPECT_CALL(services, logInfoMsg("Rail VDD STATUS_WORD: 0xbeef"))
818             .Times(1);
819         EXPECT_CALL(services, logErrorMsg("Pgood fault detected in rail VDD"))
820             .Times(1);
821         EXPECT_CALL(
822             services,
823             logErrorMsg(
824                 "Rail VDD output voltage 1.1V is <= UV fault limit 1.2V"))
825             .Times(1);
826 
827         std::string powerSupplyError{"Undervoltage fault: PSU1"};
828         std::map<std::string, std::string> additionalData{};
829         std::string error =
830             device.findPgoodFault(services, powerSupplyError, additionalData);
831         EXPECT_EQ(error,
832                   "xyz.openbmc_project.Power.Error.PowerSequencerVoltageFault");
833         EXPECT_EQ(additionalData.size(), 6);
834         EXPECT_EQ(additionalData["DEVICE_NAME"], "abc_pseq");
835         EXPECT_EQ(additionalData["GPIO_VALUES"], "[1, 1, 1]");
836         EXPECT_EQ(additionalData["RAIL_NAME"], "VDD");
837         EXPECT_EQ(additionalData["READ_VOUT"], "1.1");
838         EXPECT_EQ(additionalData["VOUT_UV_FAULT_LIMIT"], "1.2");
839         EXPECT_EQ(additionalData["STATUS_WORD"], "0xbeef");
840     }
841 
842     // Third rail has a pgood fault detected via STATUS_VOUT
843     // Device returns 0 GPIO values
844     // Does not halt pgood fault detection because GPIO values not used by rails
845     {
846         std::string name{"abc_pseq"};
847         uint8_t bus{0};
848         uint16_t address{0x23};
849         std::string powerControlGPIOName{"power-chassis-control"};
850         std::string powerGoodGPIOName{"power-chassis-good"};
851         std::vector<std::unique_ptr<Rail>> rails{};
852         rails.emplace_back(createRailStatusVout("PSU", true, 3));
853         rails.emplace_back(createRailOutputVoltage("VDD", false, 5));
854         rails.emplace_back(createRailStatusVout("VIO", false, 7));
855         MockServices services;
856         StandardDeviceImpl device{
857             name,
858             bus,
859             address,
860             powerControlGPIOName,
861             powerGoodGPIOName,
862             std::move(rails),
863             services};
864 
865         EXPECT_CALL(device, prepareForPgoodFaultDetection).Times(1);
866         std::vector<int> gpioValues{};
867         EXPECT_CALL(device, getGPIOValues)
868             .Times(1)
869             .WillOnce(Return(gpioValues));
870         EXPECT_CALL(device, getStatusVout(3)).Times(1).WillOnce(Return(0x00));
871         EXPECT_CALL(device, getReadVout(5)).Times(0);
872         EXPECT_CALL(device, getVoutUVFaultLimit(5)).Times(0);
873         EXPECT_CALL(device, getStatusVout(7)).Times(1).WillOnce(Return(0x11));
874         EXPECT_CALL(device, getStatusWord(7)).Times(1).WillOnce(Return(0xbeef));
875 
876         EXPECT_CALL(
877             services,
878             logErrorMsg(
879                 "Pgood fault found in rail monitored by device abc_pseq"))
880             .Times(1);
881         EXPECT_CALL(services, logInfoMsg("Rail VIO STATUS_WORD: 0xbeef"))
882             .Times(1);
883         EXPECT_CALL(services, logErrorMsg("Pgood fault detected in rail VIO"))
884             .Times(1);
885         EXPECT_CALL(
886             services,
887             logErrorMsg("Rail VIO has fault bits set in STATUS_VOUT: 0x11"))
888             .Times(1);
889 
890         std::string powerSupplyError{};
891         std::map<std::string, std::string> additionalData{};
892         std::string error =
893             device.findPgoodFault(services, powerSupplyError, additionalData);
894         EXPECT_EQ(error,
895                   "xyz.openbmc_project.Power.Error.PowerSequencerVoltageFault");
896         EXPECT_EQ(additionalData.size(), 4);
897         EXPECT_EQ(additionalData["DEVICE_NAME"], "abc_pseq");
898         EXPECT_EQ(additionalData["RAIL_NAME"], "VIO");
899         EXPECT_EQ(additionalData["STATUS_VOUT"], "0x11");
900         EXPECT_EQ(additionalData["STATUS_WORD"], "0xbeef");
901     }
902 
903     // Third rail has a pgood fault detected via STATUS_VOUT
904     // Exception occurs trying to obtain GPIO values from device
905     // Does not halt pgood fault detection because GPIO values not used by rails
906     {
907         std::string name{"abc_pseq"};
908         uint8_t bus{0};
909         uint16_t address{0x23};
910         std::string powerControlGPIOName{"power-chassis-control"};
911         std::string powerGoodGPIOName{"power-chassis-good"};
912         std::vector<std::unique_ptr<Rail>> rails{};
913         rails.emplace_back(createRailStatusVout("PSU", true, 3));
914         rails.emplace_back(createRailOutputVoltage("VDD", false, 5));
915         rails.emplace_back(createRailStatusVout("VIO", false, 7));
916         MockServices services;
917         StandardDeviceImpl device{
918             name,
919             bus,
920             address,
921             powerControlGPIOName,
922             powerGoodGPIOName,
923             std::move(rails),
924             services};
925 
926         EXPECT_CALL(device, prepareForPgoodFaultDetection).Times(1);
927         EXPECT_CALL(device, getGPIOValues)
928             .Times(1)
929             .WillOnce(Throw(std::runtime_error{"Unable to acquire GPIO line"}));
930         EXPECT_CALL(device, getStatusVout(3)).Times(1).WillOnce(Return(0x00));
931         EXPECT_CALL(device, getReadVout(5)).Times(0);
932         EXPECT_CALL(device, getVoutUVFaultLimit(5)).Times(0);
933         EXPECT_CALL(device, getStatusVout(7)).Times(1).WillOnce(Return(0x11));
934         EXPECT_CALL(device, getStatusWord(7)).Times(1).WillOnce(Return(0xbeef));
935 
936         EXPECT_CALL(
937             services,
938             logErrorMsg(
939                 "Pgood fault found in rail monitored by device abc_pseq"))
940             .Times(1);
941         EXPECT_CALL(services, logInfoMsg("Rail VIO STATUS_WORD: 0xbeef"))
942             .Times(1);
943         EXPECT_CALL(services, logErrorMsg("Pgood fault detected in rail VIO"))
944             .Times(1);
945         EXPECT_CALL(
946             services,
947             logErrorMsg("Rail VIO has fault bits set in STATUS_VOUT: 0x11"))
948             .Times(1);
949 
950         std::string powerSupplyError{};
951         std::map<std::string, std::string> additionalData{};
952         std::string error =
953             device.findPgoodFault(services, powerSupplyError, additionalData);
954         EXPECT_EQ(error,
955                   "xyz.openbmc_project.Power.Error.PowerSequencerVoltageFault");
956         EXPECT_EQ(additionalData.size(), 4);
957         EXPECT_EQ(additionalData["DEVICE_NAME"], "abc_pseq");
958         EXPECT_EQ(additionalData["RAIL_NAME"], "VIO");
959         EXPECT_EQ(additionalData["STATUS_VOUT"], "0x11");
960         EXPECT_EQ(additionalData["STATUS_WORD"], "0xbeef");
961     }
962 
963     // All three rails appear to have a pgood fault.  Verify third rail is
964     // selected, even though it is last in the power on sequence, because it is
965     // checked using STATUS_VOUT.  That check happens before the other checks.
966     {
967         std::string name{"abc_pseq"};
968         uint8_t bus{0};
969         uint16_t address{0x23};
970         std::string powerControlGPIOName{"power-chassis-control"};
971         std::string powerGoodGPIOName{"power-chassis-good"};
972         std::vector<std::unique_ptr<Rail>> rails{};
973         rails.emplace_back(createRailGPIO("PSU", true, 2));
974         rails.emplace_back(createRailGPIO("VDD", false, 1));
975         rails.emplace_back(createRailStatusVout("VIO", false, 7));
976         MockServices services;
977         StandardDeviceImpl device{
978             name,
979             bus,
980             address,
981             powerControlGPIOName,
982             powerGoodGPIOName,
983             std::move(rails),
984             services};
985 
986         EXPECT_CALL(device, prepareForPgoodFaultDetection).Times(1);
987         std::vector<int> gpioValues{0, 0, 0};
988         EXPECT_CALL(device, getGPIOValues)
989             .Times(1)
990             .WillOnce(Return(gpioValues));
991         EXPECT_CALL(device, getStatusVout(7)).Times(1).WillOnce(Return(0x11));
992         EXPECT_CALL(device, getStatusWord(7)).Times(1).WillOnce(Return(0xbeef));
993 
994         EXPECT_CALL(services,
995                     logInfoMsg("Device abc_pseq GPIO values: [0, 0, 0]"))
996             .Times(1);
997         EXPECT_CALL(
998             services,
999             logErrorMsg(
1000                 "Pgood fault found in rail monitored by device abc_pseq"))
1001             .Times(1);
1002         EXPECT_CALL(services, logInfoMsg("Rail VIO STATUS_WORD: 0xbeef"))
1003             .Times(1);
1004         EXPECT_CALL(services, logErrorMsg("Pgood fault detected in rail VIO"))
1005             .Times(1);
1006         EXPECT_CALL(
1007             services,
1008             logErrorMsg("Rail VIO has fault bits set in STATUS_VOUT: 0x11"))
1009             .Times(1);
1010 
1011         std::string powerSupplyError{};
1012         std::map<std::string, std::string> additionalData{};
1013         std::string error =
1014             device.findPgoodFault(services, powerSupplyError, additionalData);
1015         EXPECT_EQ(error,
1016                   "xyz.openbmc_project.Power.Error.PowerSequencerVoltageFault");
1017         EXPECT_EQ(additionalData.size(), 5);
1018         EXPECT_EQ(additionalData["DEVICE_NAME"], "abc_pseq");
1019         EXPECT_EQ(additionalData["GPIO_VALUES"], "[0, 0, 0]");
1020         EXPECT_EQ(additionalData["RAIL_NAME"], "VIO");
1021         EXPECT_EQ(additionalData["STATUS_VOUT"], "0x11");
1022         EXPECT_EQ(additionalData["STATUS_WORD"], "0xbeef");
1023     }
1024 
1025     // Two rails appear to have a pgood fault.  One is found via output voltage
1026     // and one is found via a GPIO.  Verify the first rail in the sequence with
1027     // a fault is selected.
1028     {
1029         std::string name{"abc_pseq"};
1030         uint8_t bus{0};
1031         uint16_t address{0x23};
1032         std::string powerControlGPIOName{"power-chassis-control"};
1033         std::string powerGoodGPIOName{"power-chassis-good"};
1034         std::vector<std::unique_ptr<Rail>> rails{};
1035         rails.emplace_back(createRailStatusVout("VIO", false, 7));
1036         rails.emplace_back(createRailOutputVoltage("VDD", false, 5));
1037         rails.emplace_back(createRailGPIO("PSU", true, 2));
1038         MockServices services;
1039         StandardDeviceImpl device{
1040             name,
1041             bus,
1042             address,
1043             powerControlGPIOName,
1044             powerGoodGPIOName,
1045             std::move(rails),
1046             services};
1047 
1048         EXPECT_CALL(device, prepareForPgoodFaultDetection).Times(1);
1049         std::vector<int> gpioValues{1, 1, 0};
1050         EXPECT_CALL(device, getGPIOValues)
1051             .Times(1)
1052             .WillOnce(Return(gpioValues));
1053         EXPECT_CALL(device, getStatusVout(7)).Times(1).WillOnce(Return(0x00));
1054         EXPECT_CALL(device, getReadVout(5)).Times(1).WillOnce(Return(1.1));
1055         EXPECT_CALL(device, getVoutUVFaultLimit(5))
1056             .Times(1)
1057             .WillOnce(Return(1.2));
1058         EXPECT_CALL(device, getStatusWord(5)).Times(1).WillOnce(Return(0xbeef));
1059 
1060         EXPECT_CALL(services,
1061                     logInfoMsg("Device abc_pseq GPIO values: [1, 1, 0]"))
1062             .Times(1);
1063         EXPECT_CALL(
1064             services,
1065             logErrorMsg(
1066                 "Pgood fault found in rail monitored by device abc_pseq"))
1067             .Times(1);
1068         EXPECT_CALL(services, logInfoMsg("Rail VDD STATUS_WORD: 0xbeef"))
1069             .Times(1);
1070         EXPECT_CALL(services, logErrorMsg("Pgood fault detected in rail VDD"))
1071             .Times(1);
1072         EXPECT_CALL(
1073             services,
1074             logErrorMsg(
1075                 "Rail VDD output voltage 1.1V is <= UV fault limit 1.2V"))
1076             .Times(1);
1077 
1078         std::string powerSupplyError{};
1079         std::map<std::string, std::string> additionalData{};
1080         std::string error =
1081             device.findPgoodFault(services, powerSupplyError, additionalData);
1082         EXPECT_EQ(error,
1083                   "xyz.openbmc_project.Power.Error.PowerSequencerVoltageFault");
1084         EXPECT_EQ(additionalData.size(), 6);
1085         EXPECT_EQ(additionalData["DEVICE_NAME"], "abc_pseq");
1086         EXPECT_EQ(additionalData["GPIO_VALUES"], "[1, 1, 0]");
1087         EXPECT_EQ(additionalData["RAIL_NAME"], "VDD");
1088         EXPECT_EQ(additionalData["READ_VOUT"], "1.1");
1089         EXPECT_EQ(additionalData["VOUT_UV_FAULT_LIMIT"], "1.2");
1090         EXPECT_EQ(additionalData["STATUS_WORD"], "0xbeef");
1091     }
1092 
1093     // Exception is thrown during pgood fault detection
1094     {
1095         std::string name{"abc_pseq"};
1096         uint8_t bus{0};
1097         uint16_t address{0x23};
1098         std::string powerControlGPIOName{"power-chassis-control"};
1099         std::string powerGoodGPIOName{"power-chassis-good"};
1100         std::vector<std::unique_ptr<Rail>> rails{};
1101         rails.emplace_back(createRailGPIO("PSU", true, 2));
1102         rails.emplace_back(createRailOutputVoltage("VDD", false, 5));
1103         rails.emplace_back(createRailStatusVout("VIO", false, 7));
1104         MockServices services;
1105         StandardDeviceImpl device{
1106             name,
1107             bus,
1108             address,
1109             powerControlGPIOName,
1110             powerGoodGPIOName,
1111             std::move(rails),
1112             services};
1113 
1114         EXPECT_CALL(device, prepareForPgoodFaultDetection).Times(1);
1115         std::vector<int> gpioValues{1, 1, 1};
1116         EXPECT_CALL(device, getGPIOValues)
1117             .Times(1)
1118             .WillOnce(Return(gpioValues));
1119         EXPECT_CALL(device, getReadVout(5)).Times(0);
1120         EXPECT_CALL(device, getVoutUVFaultLimit(5)).Times(0);
1121         EXPECT_CALL(device, getStatusVout(7))
1122             .Times(1)
1123             .WillOnce(Throw(std::runtime_error{"File does not exist"}));
1124 
1125         std::string powerSupplyError{};
1126         std::map<std::string, std::string> additionalData{};
1127         try
1128         {
1129             device.findPgoodFault(services, powerSupplyError, additionalData);
1130             ADD_FAILURE() << "Should not have reached this line.";
1131         }
1132         catch (const std::exception& e)
1133         {
1134             EXPECT_STREQ(
1135                 e.what(),
1136                 "Unable to determine if a pgood fault occurred in device abc_pseq: "
1137                 "Unable to read STATUS_VOUT value for rail VIO: "
1138                 "File does not exist");
1139         }
1140     }
1141 }
1142