xref: /openbmc/phosphor-power/phosphor-power-sequencer/test/standard_device_tests.cpp (revision 7b7a5632c594d60f4620ca14379a766a56faf846)
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_services.hpp"
18 #include "rail.hpp"
19 #include "services.hpp"
20 #include "standard_device.hpp"
21 
22 #include <cstdint>
23 #include <map>
24 #include <memory>
25 #include <optional>
26 #include <string>
27 #include <utility>
28 #include <vector>
29 
30 #include <gmock/gmock.h>
31 #include <gtest/gtest.h>
32 
33 using namespace phosphor::power::sequencer;
34 
35 using ::testing::Return;
36 using ::testing::Throw;
37 
38 /**
39  * @class StandardDeviceImpl
40  *
41  * Concrete subclass of the StandardDevice abstract class.
42  *
43  * This subclass is required for two reasons:
44  * - StandardDevice has some pure virtual methods so it cannot be instantiated.
45  * - The pure virtual methods provide the PMBus and GPIO information.  Mocking
46  *   these makes it possible to test the pgood fault detection algorithm.
47  *
48  * This class is not intended to be used outside of this file.  It is
49  * implementation detail for testing the the StandardDevice class.
50  */
51 class StandardDeviceImpl : public StandardDevice
52 {
53   public:
54     // Specify which compiler-generated methods we want
55     StandardDeviceImpl() = delete;
56     StandardDeviceImpl(const StandardDeviceImpl&) = delete;
57     StandardDeviceImpl(StandardDeviceImpl&&) = delete;
58     StandardDeviceImpl& operator=(const StandardDeviceImpl&) = delete;
59     StandardDeviceImpl& operator=(StandardDeviceImpl&&) = delete;
60     virtual ~StandardDeviceImpl() = default;
61 
62     // Constructor just calls StandardDevice constructor
63     explicit StandardDeviceImpl(const std::string& name, uint8_t bus,
64                                 uint16_t address,
65                                 const std::string& powerControlGPIOName,
66                                 const std::string& powerGoodGPIOName,
67                                 std::vector<std::unique_ptr<Rail>> rails) :
68         StandardDevice(name, bus, address, powerControlGPIOName,
69                        powerGoodGPIOName, std::move(rails))
70     {}
71 
72     // Mock pure virtual methods
73     MOCK_METHOD(std::vector<int>, getGPIOValues, (Services & services),
74                 (override));
75     MOCK_METHOD(uint16_t, getStatusWord, (uint8_t page), (override));
76     MOCK_METHOD(uint8_t, getStatusVout, (uint8_t page), (override));
77     MOCK_METHOD(double, getReadVout, (uint8_t page), (override));
78     MOCK_METHOD(double, getVoutUVFaultLimit, (uint8_t page), (override));
79 
80     // Override empty implementation with mock so we can verify it is called
81     MOCK_METHOD(void, prepareForPgoodFaultDetection, (Services & services),
82                 (override));
83 };
84 
85 /**
86  * Creates a Rail object that checks for a pgood fault using STATUS_VOUT.
87  *
88  * @param name Unique name for the rail
89  * @param isPowerSupplyRail Specifies whether the rail is produced by a
90                             power supply
91  * @param pageNum PMBus PAGE number of the rail
92  * @return Rail object
93  */
94 std::unique_ptr<Rail> createRailStatusVout(
95     const std::string& name, bool isPowerSupplyRail, uint8_t pageNum)
96 {
97     std::optional<std::string> presence{};
98     std::optional<uint8_t> page{pageNum};
99     bool checkStatusVout{true};
100     bool compareVoltageToLimit{false};
101     std::optional<PgoodGPIO> gpio{};
102     return std::make_unique<Rail>(name, presence, page, isPowerSupplyRail,
103                                   checkStatusVout, compareVoltageToLimit, gpio);
104 }
105 
106 /**
107  * Creates a Rail object that checks for a pgood fault using a GPIO.
108  *
109  * @param name Unique name for the rail
110  * @param isPowerSupplyRail Specifies whether the rail is produced by a
111                             power supply
112  * @param gpio GPIO line to read to determine the pgood status of the rail
113  * @return Rail object
114  */
115 std::unique_ptr<Rail> createRailGPIO(
116     const std::string& name, bool isPowerSupplyRail, unsigned int gpioLine)
117 {
118     std::optional<std::string> presence{};
119     std::optional<uint8_t> page{};
120     bool checkStatusVout{false};
121     bool compareVoltageToLimit{false};
122     bool activeLow{false};
123     std::optional<PgoodGPIO> gpio{PgoodGPIO{gpioLine, activeLow}};
124     return std::make_unique<Rail>(name, presence, page, isPowerSupplyRail,
125                                   checkStatusVout, compareVoltageToLimit, gpio);
126 }
127 
128 /**
129  * Creates a Rail object that checks for a pgood fault using output voltage.
130  *
131  * @param name Unique name for the rail
132  * @param isPowerSupplyRail Specifies whether the rail is produced by a
133                             power supply
134  * @param pageNum PMBus PAGE number of the rail
135  * @return Rail object
136  */
137 std::unique_ptr<Rail> createRailOutputVoltage(
138     const std::string& name, bool isPowerSupplyRail, uint8_t pageNum)
139 {
140     std::optional<std::string> presence{};
141     std::optional<uint8_t> page{pageNum};
142     bool checkStatusVout{false};
143     bool compareVoltageToLimit{true};
144     std::optional<PgoodGPIO> gpio{};
145     return std::make_unique<Rail>(name, presence, page, isPowerSupplyRail,
146                                   checkStatusVout, compareVoltageToLimit, gpio);
147 }
148 
149 TEST(StandardDeviceTests, Constructor)
150 {
151     // Empty vector of rails
152     {
153         std::string name{"xyz_pseq"};
154         uint8_t bus{3};
155         uint16_t address{0x72};
156         std::string powerControlGPIOName{"power-chassis-control"};
157         std::string powerGoodGPIOName{"power-chassis-good"};
158         std::vector<std::unique_ptr<Rail>> rails{};
159         StandardDeviceImpl device{
160             name,
161             bus,
162             address,
163             powerControlGPIOName,
164             powerGoodGPIOName,
165             std::move(rails)};
166 
167         EXPECT_EQ(device.getName(), name);
168         EXPECT_EQ(device.getBus(), bus);
169         EXPECT_EQ(device.getAddress(), address);
170         EXPECT_EQ(device.getPowerControlGPIOName(), powerControlGPIOName);
171         EXPECT_EQ(device.getPowerGoodGPIOName(), powerGoodGPIOName);
172         EXPECT_TRUE(device.getRails().empty());
173     }
174 
175     // Non-empty vector of rails
176     {
177         std::string name{"abc_pseq"};
178         uint8_t bus{0};
179         uint16_t address{0x23};
180         std::string powerControlGPIOName{"power-chassis-control"};
181         std::string powerGoodGPIOName{"power-chassis-good"};
182         std::vector<std::unique_ptr<Rail>> rails{};
183         rails.emplace_back(createRailGPIO("PSU", true, 3));
184         rails.emplace_back(createRailOutputVoltage("VDD", false, 5));
185         rails.emplace_back(createRailStatusVout("VIO", false, 7));
186         StandardDeviceImpl device{
187             name,
188             bus,
189             address,
190             powerControlGPIOName,
191             powerGoodGPIOName,
192             std::move(rails)};
193 
194         EXPECT_EQ(device.getName(), name);
195         EXPECT_EQ(device.getBus(), bus);
196         EXPECT_EQ(device.getAddress(), address);
197         EXPECT_EQ(device.getPowerControlGPIOName(), powerControlGPIOName);
198         EXPECT_EQ(device.getPowerGoodGPIOName(), powerGoodGPIOName);
199         EXPECT_EQ(device.getRails().size(), 3);
200         EXPECT_EQ(device.getRails()[0]->getName(), "PSU");
201         EXPECT_EQ(device.getRails()[1]->getName(), "VDD");
202         EXPECT_EQ(device.getRails()[2]->getName(), "VIO");
203     }
204 }
205 
206 TEST(StandardDeviceTests, GetName)
207 {
208     std::string name{"xyz_pseq"};
209     uint8_t bus{0};
210     uint16_t address{0x23};
211     std::string powerControlGPIOName{"power-chassis-control"};
212     std::string powerGoodGPIOName{"power-chassis-good"};
213     std::vector<std::unique_ptr<Rail>> rails{};
214     StandardDeviceImpl device{
215         name,
216         bus,
217         address,
218         powerControlGPIOName,
219         powerGoodGPIOName,
220         std::move(rails)};
221 
222     EXPECT_EQ(device.getName(), name);
223 }
224 
225 TEST(StandardDeviceTests, GetBus)
226 {
227     std::string name{"abc_pseq"};
228     uint8_t bus{1};
229     uint16_t address{0x23};
230     std::string powerControlGPIOName{"power-chassis-control"};
231     std::string powerGoodGPIOName{"power-chassis-good"};
232     std::vector<std::unique_ptr<Rail>> rails{};
233     StandardDeviceImpl device{
234         name,
235         bus,
236         address,
237         powerControlGPIOName,
238         powerGoodGPIOName,
239         std::move(rails)};
240 
241     EXPECT_EQ(device.getBus(), bus);
242 }
243 
244 TEST(StandardDeviceTests, GetAddress)
245 {
246     std::string name{"abc_pseq"};
247     uint8_t bus{1};
248     uint16_t address{0x24};
249     std::string powerControlGPIOName{"power-chassis-control"};
250     std::string powerGoodGPIOName{"power-chassis-good"};
251     std::vector<std::unique_ptr<Rail>> rails{};
252     StandardDeviceImpl device{
253         name,
254         bus,
255         address,
256         powerControlGPIOName,
257         powerGoodGPIOName,
258         std::move(rails)};
259 
260     EXPECT_EQ(device.getAddress(), address);
261 }
262 
263 TEST(StandardDeviceTests, GetPowerControlGPIOName)
264 {
265     std::string name{"xyz_pseq"};
266     uint8_t bus{0};
267     uint16_t address{0x23};
268     std::string powerControlGPIOName{"power-on"};
269     std::string powerGoodGPIOName{"chassis-pgood"};
270     std::vector<std::unique_ptr<Rail>> rails{};
271     StandardDeviceImpl device{
272         name,
273         bus,
274         address,
275         powerControlGPIOName,
276         powerGoodGPIOName,
277         std::move(rails)};
278 
279     EXPECT_EQ(device.getPowerControlGPIOName(), powerControlGPIOName);
280 }
281 
282 TEST(StandardDeviceTests, GetPowerGoodGPIOName)
283 {
284     std::string name{"xyz_pseq"};
285     uint8_t bus{0};
286     uint16_t address{0x23};
287     std::string powerControlGPIOName{"power-on"};
288     std::string powerGoodGPIOName{"chassis-pgood"};
289     std::vector<std::unique_ptr<Rail>> rails{};
290     StandardDeviceImpl device{
291         name,
292         bus,
293         address,
294         powerControlGPIOName,
295         powerGoodGPIOName,
296         std::move(rails)};
297 
298     EXPECT_EQ(device.getPowerGoodGPIOName(), powerGoodGPIOName);
299 }
300 
301 TEST(StandardDeviceTests, GetRails)
302 {
303     // Empty vector of rails
304     {
305         std::string name{"xyz_pseq"};
306         uint8_t bus{0};
307         uint16_t address{0x23};
308         std::string powerControlGPIOName{"power-chassis-control"};
309         std::string powerGoodGPIOName{"power-chassis-good"};
310         std::vector<std::unique_ptr<Rail>> rails{};
311         StandardDeviceImpl device{
312             name,
313             bus,
314             address,
315             powerControlGPIOName,
316             powerGoodGPIOName,
317             std::move(rails)};
318 
319         EXPECT_TRUE(device.getRails().empty());
320     }
321 
322     // Non-empty vector of rails
323     {
324         std::string name{"abc_pseq"};
325         uint8_t bus{0};
326         uint16_t address{0x23};
327         std::string powerControlGPIOName{"power-chassis-control"};
328         std::string powerGoodGPIOName{"power-chassis-good"};
329         std::vector<std::unique_ptr<Rail>> rails{};
330         rails.emplace_back(createRailGPIO("PSU", true, 3));
331         rails.emplace_back(createRailOutputVoltage("VDD", false, 5));
332         rails.emplace_back(createRailStatusVout("VIO", false, 7));
333         StandardDeviceImpl device{
334             name,
335             bus,
336             address,
337             powerControlGPIOName,
338             powerGoodGPIOName,
339             std::move(rails)};
340 
341         EXPECT_EQ(device.getRails().size(), 3);
342         EXPECT_EQ(device.getRails()[0]->getName(), "PSU");
343         EXPECT_EQ(device.getRails()[1]->getName(), "VDD");
344         EXPECT_EQ(device.getRails()[2]->getName(), "VIO");
345     }
346 }
347 
348 TEST(StandardDeviceTests, FindPgoodFault)
349 {
350     // No rail has a pgood fault
351     {
352         std::string name{"abc_pseq"};
353         uint8_t bus{0};
354         uint16_t address{0x23};
355         std::string powerControlGPIOName{"power-chassis-control"};
356         std::string powerGoodGPIOName{"power-chassis-good"};
357         std::vector<std::unique_ptr<Rail>> rails{};
358         rails.emplace_back(createRailGPIO("PSU", true, 2));
359         rails.emplace_back(createRailOutputVoltage("VDD", false, 5));
360         rails.emplace_back(createRailStatusVout("VIO", false, 7));
361         StandardDeviceImpl device{
362             name,
363             bus,
364             address,
365             powerControlGPIOName,
366             powerGoodGPIOName,
367             std::move(rails)};
368 
369         EXPECT_CALL(device, prepareForPgoodFaultDetection).Times(1);
370         std::vector<int> gpioValues{1, 1, 1};
371         EXPECT_CALL(device, getGPIOValues)
372             .Times(1)
373             .WillOnce(Return(gpioValues));
374         EXPECT_CALL(device, getReadVout(5)).Times(1).WillOnce(Return(1.2));
375         EXPECT_CALL(device, getVoutUVFaultLimit(5))
376             .Times(1)
377             .WillOnce(Return(1.1));
378         EXPECT_CALL(device, getStatusVout(7)).Times(1).WillOnce(Return(0x00));
379 
380         MockServices services{};
381 
382         std::string powerSupplyError{};
383         std::map<std::string, std::string> additionalData{};
384         std::string error =
385             device.findPgoodFault(services, powerSupplyError, additionalData);
386         EXPECT_TRUE(error.empty());
387         EXPECT_EQ(additionalData.size(), 0);
388     }
389 
390     // First rail has a pgood fault detected via GPIO
391     // Is a PSU rail: No PSU error specified
392     {
393         std::string name{"abc_pseq"};
394         uint8_t bus{0};
395         uint16_t address{0x23};
396         std::string powerControlGPIOName{"power-chassis-control"};
397         std::string powerGoodGPIOName{"power-chassis-good"};
398         std::vector<std::unique_ptr<Rail>> rails{};
399         rails.emplace_back(createRailGPIO("PSU", true, 2));
400         rails.emplace_back(createRailOutputVoltage("VDD", false, 5));
401         rails.emplace_back(createRailStatusVout("VIO", false, 7));
402         StandardDeviceImpl device{
403             name,
404             bus,
405             address,
406             powerControlGPIOName,
407             powerGoodGPIOName,
408             std::move(rails)};
409 
410         EXPECT_CALL(device, prepareForPgoodFaultDetection).Times(1);
411         std::vector<int> gpioValues{1, 1, 0};
412         EXPECT_CALL(device, getGPIOValues)
413             .Times(1)
414             .WillOnce(Return(gpioValues));
415         EXPECT_CALL(device, getReadVout(5)).Times(0);
416         EXPECT_CALL(device, getVoutUVFaultLimit(5)).Times(0);
417         EXPECT_CALL(device, getStatusVout(7)).Times(1).WillOnce(Return(0x00));
418 
419         MockServices services{};
420         EXPECT_CALL(services,
421                     logInfoMsg("Device abc_pseq GPIO values: [1, 1, 0]"))
422             .Times(1);
423         EXPECT_CALL(
424             services,
425             logErrorMsg(
426                 "Pgood fault found in rail monitored by device abc_pseq"))
427             .Times(1);
428         EXPECT_CALL(services, logErrorMsg("Pgood fault detected in rail PSU"))
429             .Times(1);
430         EXPECT_CALL(
431             services,
432             logErrorMsg(
433                 "Rail PSU pgood GPIO line offset 2 has inactive value 0"))
434             .Times(1);
435 
436         std::string powerSupplyError{};
437         std::map<std::string, std::string> additionalData{};
438         std::string error =
439             device.findPgoodFault(services, powerSupplyError, additionalData);
440         EXPECT_EQ(error,
441                   "xyz.openbmc_project.Power.Error.PowerSequencerVoltageFault");
442         EXPECT_EQ(additionalData.size(), 5);
443         EXPECT_EQ(additionalData["DEVICE_NAME"], "abc_pseq");
444         EXPECT_EQ(additionalData["GPIO_VALUES"], "[1, 1, 0]");
445         EXPECT_EQ(additionalData["RAIL_NAME"], "PSU");
446         EXPECT_EQ(additionalData["GPIO_LINE"], "2");
447         EXPECT_EQ(additionalData["GPIO_VALUE"], "0");
448     }
449 
450     // First rail has a pgood fault detected via GPIO
451     // Is a PSU rail: PSU error specified
452     {
453         std::string name{"abc_pseq"};
454         uint8_t bus{0};
455         uint16_t address{0x23};
456         std::string powerControlGPIOName{"power-chassis-control"};
457         std::string powerGoodGPIOName{"power-chassis-good"};
458         std::vector<std::unique_ptr<Rail>> rails{};
459         rails.emplace_back(createRailGPIO("PSU", true, 2));
460         rails.emplace_back(createRailOutputVoltage("VDD", false, 5));
461         rails.emplace_back(createRailStatusVout("VIO", false, 7));
462         StandardDeviceImpl device{
463             name,
464             bus,
465             address,
466             powerControlGPIOName,
467             powerGoodGPIOName,
468             std::move(rails)};
469 
470         EXPECT_CALL(device, prepareForPgoodFaultDetection).Times(1);
471         std::vector<int> gpioValues{1, 1, 0};
472         EXPECT_CALL(device, getGPIOValues)
473             .Times(1)
474             .WillOnce(Return(gpioValues));
475         EXPECT_CALL(device, getReadVout(5)).Times(0);
476         EXPECT_CALL(device, getVoutUVFaultLimit(5)).Times(0);
477         EXPECT_CALL(device, getStatusVout(7)).Times(1).WillOnce(Return(0x00));
478 
479         MockServices services{};
480         EXPECT_CALL(services,
481                     logInfoMsg("Device abc_pseq GPIO values: [1, 1, 0]"))
482             .Times(1);
483         EXPECT_CALL(
484             services,
485             logErrorMsg(
486                 "Pgood fault found in rail monitored by device abc_pseq"))
487             .Times(1);
488         EXPECT_CALL(services, logErrorMsg("Pgood fault detected in rail PSU"))
489             .Times(1);
490         EXPECT_CALL(
491             services,
492             logErrorMsg(
493                 "Rail PSU pgood GPIO line offset 2 has inactive value 0"))
494             .Times(1);
495 
496         std::string powerSupplyError{"Undervoltage fault: PSU1"};
497         std::map<std::string, std::string> additionalData{};
498         std::string error =
499             device.findPgoodFault(services, powerSupplyError, additionalData);
500         EXPECT_EQ(error, powerSupplyError);
501         EXPECT_EQ(additionalData.size(), 5);
502         EXPECT_EQ(additionalData["DEVICE_NAME"], "abc_pseq");
503         EXPECT_EQ(additionalData["GPIO_VALUES"], "[1, 1, 0]");
504         EXPECT_EQ(additionalData["RAIL_NAME"], "PSU");
505         EXPECT_EQ(additionalData["GPIO_LINE"], "2");
506         EXPECT_EQ(additionalData["GPIO_VALUE"], "0");
507     }
508 
509     // Second rail has a pgood fault detected via output voltage
510     // Not a PSU rail: PSU error specified
511     {
512         std::string name{"abc_pseq"};
513         uint8_t bus{0};
514         uint16_t address{0x23};
515         std::string powerControlGPIOName{"power-chassis-control"};
516         std::string powerGoodGPIOName{"power-chassis-good"};
517         std::vector<std::unique_ptr<Rail>> rails{};
518         rails.emplace_back(createRailGPIO("PSU", true, 2));
519         rails.emplace_back(createRailOutputVoltage("VDD", false, 5));
520         rails.emplace_back(createRailStatusVout("VIO", false, 7));
521         StandardDeviceImpl device{
522             name,
523             bus,
524             address,
525             powerControlGPIOName,
526             powerGoodGPIOName,
527             std::move(rails)};
528 
529         EXPECT_CALL(device, prepareForPgoodFaultDetection).Times(1);
530         std::vector<int> gpioValues{1, 1, 1};
531         EXPECT_CALL(device, getGPIOValues)
532             .Times(1)
533             .WillOnce(Return(gpioValues));
534         EXPECT_CALL(device, getReadVout(5)).Times(1).WillOnce(Return(1.1));
535         EXPECT_CALL(device, getVoutUVFaultLimit(5))
536             .Times(1)
537             .WillOnce(Return(1.2));
538         EXPECT_CALL(device, getStatusVout(7)).Times(1).WillOnce(Return(0x00));
539         EXPECT_CALL(device, getStatusWord(5)).Times(1).WillOnce(Return(0xbeef));
540 
541         MockServices services{};
542         EXPECT_CALL(services,
543                     logInfoMsg("Device abc_pseq GPIO values: [1, 1, 1]"))
544             .Times(1);
545         EXPECT_CALL(
546             services,
547             logErrorMsg(
548                 "Pgood fault found in rail monitored by device abc_pseq"))
549             .Times(1);
550         EXPECT_CALL(services, logInfoMsg("Rail VDD STATUS_WORD: 0xbeef"))
551             .Times(1);
552         EXPECT_CALL(services, logErrorMsg("Pgood fault detected in rail VDD"))
553             .Times(1);
554         EXPECT_CALL(
555             services,
556             logErrorMsg(
557                 "Rail VDD output voltage 1.1V is <= UV fault limit 1.2V"))
558             .Times(1);
559 
560         std::string powerSupplyError{"Undervoltage fault: PSU1"};
561         std::map<std::string, std::string> additionalData{};
562         std::string error =
563             device.findPgoodFault(services, powerSupplyError, additionalData);
564         EXPECT_EQ(error,
565                   "xyz.openbmc_project.Power.Error.PowerSequencerVoltageFault");
566         EXPECT_EQ(additionalData.size(), 6);
567         EXPECT_EQ(additionalData["DEVICE_NAME"], "abc_pseq");
568         EXPECT_EQ(additionalData["GPIO_VALUES"], "[1, 1, 1]");
569         EXPECT_EQ(additionalData["RAIL_NAME"], "VDD");
570         EXPECT_EQ(additionalData["READ_VOUT"], "1.1");
571         EXPECT_EQ(additionalData["VOUT_UV_FAULT_LIMIT"], "1.2");
572         EXPECT_EQ(additionalData["STATUS_WORD"], "0xbeef");
573     }
574 
575     // Third rail has a pgood fault detected via STATUS_VOUT
576     // Device returns 0 GPIO values
577     // Does not halt pgood fault detection because GPIO values not used by rails
578     {
579         std::string name{"abc_pseq"};
580         uint8_t bus{0};
581         uint16_t address{0x23};
582         std::string powerControlGPIOName{"power-chassis-control"};
583         std::string powerGoodGPIOName{"power-chassis-good"};
584         std::vector<std::unique_ptr<Rail>> rails{};
585         rails.emplace_back(createRailStatusVout("PSU", true, 3));
586         rails.emplace_back(createRailOutputVoltage("VDD", false, 5));
587         rails.emplace_back(createRailStatusVout("VIO", false, 7));
588         StandardDeviceImpl device{
589             name,
590             bus,
591             address,
592             powerControlGPIOName,
593             powerGoodGPIOName,
594             std::move(rails)};
595 
596         EXPECT_CALL(device, prepareForPgoodFaultDetection).Times(1);
597         std::vector<int> gpioValues{};
598         EXPECT_CALL(device, getGPIOValues)
599             .Times(1)
600             .WillOnce(Return(gpioValues));
601         EXPECT_CALL(device, getStatusVout(3)).Times(1).WillOnce(Return(0x00));
602         EXPECT_CALL(device, getReadVout(5)).Times(0);
603         EXPECT_CALL(device, getVoutUVFaultLimit(5)).Times(0);
604         EXPECT_CALL(device, getStatusVout(7)).Times(1).WillOnce(Return(0x11));
605         EXPECT_CALL(device, getStatusWord(7)).Times(1).WillOnce(Return(0xbeef));
606 
607         MockServices services{};
608         EXPECT_CALL(
609             services,
610             logErrorMsg(
611                 "Pgood fault found in rail monitored by device abc_pseq"))
612             .Times(1);
613         EXPECT_CALL(services, logInfoMsg("Rail VIO STATUS_WORD: 0xbeef"))
614             .Times(1);
615         EXPECT_CALL(services, logErrorMsg("Pgood fault detected in rail VIO"))
616             .Times(1);
617         EXPECT_CALL(
618             services,
619             logErrorMsg("Rail VIO has fault bits set in STATUS_VOUT: 0x11"))
620             .Times(1);
621 
622         std::string powerSupplyError{};
623         std::map<std::string, std::string> additionalData{};
624         std::string error =
625             device.findPgoodFault(services, powerSupplyError, additionalData);
626         EXPECT_EQ(error,
627                   "xyz.openbmc_project.Power.Error.PowerSequencerVoltageFault");
628         EXPECT_EQ(additionalData.size(), 4);
629         EXPECT_EQ(additionalData["DEVICE_NAME"], "abc_pseq");
630         EXPECT_EQ(additionalData["RAIL_NAME"], "VIO");
631         EXPECT_EQ(additionalData["STATUS_VOUT"], "0x11");
632         EXPECT_EQ(additionalData["STATUS_WORD"], "0xbeef");
633     }
634 
635     // Third rail has a pgood fault detected via STATUS_VOUT
636     // Exception occurs trying to obtain GPIO values from device
637     // Does not halt pgood fault detection because GPIO values not used by rails
638     {
639         std::string name{"abc_pseq"};
640         uint8_t bus{0};
641         uint16_t address{0x23};
642         std::string powerControlGPIOName{"power-chassis-control"};
643         std::string powerGoodGPIOName{"power-chassis-good"};
644         std::vector<std::unique_ptr<Rail>> rails{};
645         rails.emplace_back(createRailStatusVout("PSU", true, 3));
646         rails.emplace_back(createRailOutputVoltage("VDD", false, 5));
647         rails.emplace_back(createRailStatusVout("VIO", false, 7));
648         StandardDeviceImpl device{
649             name,
650             bus,
651             address,
652             powerControlGPIOName,
653             powerGoodGPIOName,
654             std::move(rails)};
655 
656         EXPECT_CALL(device, prepareForPgoodFaultDetection).Times(1);
657         EXPECT_CALL(device, getGPIOValues)
658             .Times(1)
659             .WillOnce(Throw(std::runtime_error{"Unable to acquire GPIO line"}));
660         EXPECT_CALL(device, getStatusVout(3)).Times(1).WillOnce(Return(0x00));
661         EXPECT_CALL(device, getReadVout(5)).Times(0);
662         EXPECT_CALL(device, getVoutUVFaultLimit(5)).Times(0);
663         EXPECT_CALL(device, getStatusVout(7)).Times(1).WillOnce(Return(0x11));
664         EXPECT_CALL(device, getStatusWord(7)).Times(1).WillOnce(Return(0xbeef));
665 
666         MockServices services{};
667         EXPECT_CALL(
668             services,
669             logErrorMsg(
670                 "Pgood fault found in rail monitored by device abc_pseq"))
671             .Times(1);
672         EXPECT_CALL(services, logInfoMsg("Rail VIO STATUS_WORD: 0xbeef"))
673             .Times(1);
674         EXPECT_CALL(services, logErrorMsg("Pgood fault detected in rail VIO"))
675             .Times(1);
676         EXPECT_CALL(
677             services,
678             logErrorMsg("Rail VIO has fault bits set in STATUS_VOUT: 0x11"))
679             .Times(1);
680 
681         std::string powerSupplyError{};
682         std::map<std::string, std::string> additionalData{};
683         std::string error =
684             device.findPgoodFault(services, powerSupplyError, additionalData);
685         EXPECT_EQ(error,
686                   "xyz.openbmc_project.Power.Error.PowerSequencerVoltageFault");
687         EXPECT_EQ(additionalData.size(), 4);
688         EXPECT_EQ(additionalData["DEVICE_NAME"], "abc_pseq");
689         EXPECT_EQ(additionalData["RAIL_NAME"], "VIO");
690         EXPECT_EQ(additionalData["STATUS_VOUT"], "0x11");
691         EXPECT_EQ(additionalData["STATUS_WORD"], "0xbeef");
692     }
693 
694     // All three rails appear to have a pgood fault.  Verify third rail is
695     // selected, even though it is last in the power on sequence, because it is
696     // checked using STATUS_VOUT.  That check happens before the other checks.
697     {
698         std::string name{"abc_pseq"};
699         uint8_t bus{0};
700         uint16_t address{0x23};
701         std::string powerControlGPIOName{"power-chassis-control"};
702         std::string powerGoodGPIOName{"power-chassis-good"};
703         std::vector<std::unique_ptr<Rail>> rails{};
704         rails.emplace_back(createRailGPIO("PSU", true, 2));
705         rails.emplace_back(createRailGPIO("VDD", false, 1));
706         rails.emplace_back(createRailStatusVout("VIO", false, 7));
707         StandardDeviceImpl device{
708             name,
709             bus,
710             address,
711             powerControlGPIOName,
712             powerGoodGPIOName,
713             std::move(rails)};
714 
715         EXPECT_CALL(device, prepareForPgoodFaultDetection).Times(1);
716         std::vector<int> gpioValues{0, 0, 0};
717         EXPECT_CALL(device, getGPIOValues)
718             .Times(1)
719             .WillOnce(Return(gpioValues));
720         EXPECT_CALL(device, getStatusVout(7)).Times(1).WillOnce(Return(0x11));
721         EXPECT_CALL(device, getStatusWord(7)).Times(1).WillOnce(Return(0xbeef));
722 
723         MockServices services{};
724         EXPECT_CALL(services,
725                     logInfoMsg("Device abc_pseq GPIO values: [0, 0, 0]"))
726             .Times(1);
727         EXPECT_CALL(
728             services,
729             logErrorMsg(
730                 "Pgood fault found in rail monitored by device abc_pseq"))
731             .Times(1);
732         EXPECT_CALL(services, logInfoMsg("Rail VIO STATUS_WORD: 0xbeef"))
733             .Times(1);
734         EXPECT_CALL(services, logErrorMsg("Pgood fault detected in rail VIO"))
735             .Times(1);
736         EXPECT_CALL(
737             services,
738             logErrorMsg("Rail VIO has fault bits set in STATUS_VOUT: 0x11"))
739             .Times(1);
740 
741         std::string powerSupplyError{};
742         std::map<std::string, std::string> additionalData{};
743         std::string error =
744             device.findPgoodFault(services, powerSupplyError, additionalData);
745         EXPECT_EQ(error,
746                   "xyz.openbmc_project.Power.Error.PowerSequencerVoltageFault");
747         EXPECT_EQ(additionalData.size(), 5);
748         EXPECT_EQ(additionalData["DEVICE_NAME"], "abc_pseq");
749         EXPECT_EQ(additionalData["GPIO_VALUES"], "[0, 0, 0]");
750         EXPECT_EQ(additionalData["RAIL_NAME"], "VIO");
751         EXPECT_EQ(additionalData["STATUS_VOUT"], "0x11");
752         EXPECT_EQ(additionalData["STATUS_WORD"], "0xbeef");
753     }
754 
755     // Two rails appear to have a pgood fault.  One is found via output voltage
756     // and one is found via a GPIO.  Verify the first rail in the sequence with
757     // a fault is selected.
758     {
759         std::string name{"abc_pseq"};
760         uint8_t bus{0};
761         uint16_t address{0x23};
762         std::string powerControlGPIOName{"power-chassis-control"};
763         std::string powerGoodGPIOName{"power-chassis-good"};
764         std::vector<std::unique_ptr<Rail>> rails{};
765         rails.emplace_back(createRailStatusVout("VIO", false, 7));
766         rails.emplace_back(createRailOutputVoltage("VDD", false, 5));
767         rails.emplace_back(createRailGPIO("PSU", true, 2));
768         StandardDeviceImpl device{
769             name,
770             bus,
771             address,
772             powerControlGPIOName,
773             powerGoodGPIOName,
774             std::move(rails)};
775 
776         EXPECT_CALL(device, prepareForPgoodFaultDetection).Times(1);
777         std::vector<int> gpioValues{1, 1, 0};
778         EXPECT_CALL(device, getGPIOValues)
779             .Times(1)
780             .WillOnce(Return(gpioValues));
781         EXPECT_CALL(device, getStatusVout(7)).Times(1).WillOnce(Return(0x00));
782         EXPECT_CALL(device, getReadVout(5)).Times(1).WillOnce(Return(1.1));
783         EXPECT_CALL(device, getVoutUVFaultLimit(5))
784             .Times(1)
785             .WillOnce(Return(1.2));
786         EXPECT_CALL(device, getStatusWord(5)).Times(1).WillOnce(Return(0xbeef));
787 
788         MockServices services{};
789         EXPECT_CALL(services,
790                     logInfoMsg("Device abc_pseq GPIO values: [1, 1, 0]"))
791             .Times(1);
792         EXPECT_CALL(
793             services,
794             logErrorMsg(
795                 "Pgood fault found in rail monitored by device abc_pseq"))
796             .Times(1);
797         EXPECT_CALL(services, logInfoMsg("Rail VDD STATUS_WORD: 0xbeef"))
798             .Times(1);
799         EXPECT_CALL(services, logErrorMsg("Pgood fault detected in rail VDD"))
800             .Times(1);
801         EXPECT_CALL(
802             services,
803             logErrorMsg(
804                 "Rail VDD output voltage 1.1V is <= UV fault limit 1.2V"))
805             .Times(1);
806 
807         std::string powerSupplyError{};
808         std::map<std::string, std::string> additionalData{};
809         std::string error =
810             device.findPgoodFault(services, powerSupplyError, additionalData);
811         EXPECT_EQ(error,
812                   "xyz.openbmc_project.Power.Error.PowerSequencerVoltageFault");
813         EXPECT_EQ(additionalData.size(), 6);
814         EXPECT_EQ(additionalData["DEVICE_NAME"], "abc_pseq");
815         EXPECT_EQ(additionalData["GPIO_VALUES"], "[1, 1, 0]");
816         EXPECT_EQ(additionalData["RAIL_NAME"], "VDD");
817         EXPECT_EQ(additionalData["READ_VOUT"], "1.1");
818         EXPECT_EQ(additionalData["VOUT_UV_FAULT_LIMIT"], "1.2");
819         EXPECT_EQ(additionalData["STATUS_WORD"], "0xbeef");
820     }
821 
822     // Exception is thrown during pgood fault detection
823     {
824         std::string name{"abc_pseq"};
825         uint8_t bus{0};
826         uint16_t address{0x23};
827         std::string powerControlGPIOName{"power-chassis-control"};
828         std::string powerGoodGPIOName{"power-chassis-good"};
829         std::vector<std::unique_ptr<Rail>> rails{};
830         rails.emplace_back(createRailGPIO("PSU", true, 2));
831         rails.emplace_back(createRailOutputVoltage("VDD", false, 5));
832         rails.emplace_back(createRailStatusVout("VIO", false, 7));
833         StandardDeviceImpl device{
834             name,
835             bus,
836             address,
837             powerControlGPIOName,
838             powerGoodGPIOName,
839             std::move(rails)};
840 
841         EXPECT_CALL(device, prepareForPgoodFaultDetection).Times(1);
842         std::vector<int> gpioValues{1, 1, 1};
843         EXPECT_CALL(device, getGPIOValues)
844             .Times(1)
845             .WillOnce(Return(gpioValues));
846         EXPECT_CALL(device, getReadVout(5)).Times(0);
847         EXPECT_CALL(device, getVoutUVFaultLimit(5)).Times(0);
848         EXPECT_CALL(device, getStatusVout(7))
849             .Times(1)
850             .WillOnce(Throw(std::runtime_error{"File does not exist"}));
851 
852         MockServices services{};
853 
854         std::string powerSupplyError{};
855         std::map<std::string, std::string> additionalData{};
856         try
857         {
858             device.findPgoodFault(services, powerSupplyError, additionalData);
859             ADD_FAILURE() << "Should not have reached this line.";
860         }
861         catch (const std::exception& e)
862         {
863             EXPECT_STREQ(
864                 e.what(),
865                 "Unable to determine if a pgood fault occurred in device abc_pseq: "
866                 "Unable to read STATUS_VOUT value for rail VIO: "
867                 "File does not exist");
868         }
869     }
870 }
871