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_device.hpp"
18 #include "mock_services.hpp"
19 #include "rail.hpp"
20 
21 #include <cstdint>
22 #include <map>
23 #include <optional>
24 #include <string>
25 #include <vector>
26 
27 #include <gmock/gmock.h>
28 #include <gtest/gtest.h>
29 
30 using namespace phosphor::power::sequencer;
31 
32 using ::testing::Return;
33 using ::testing::Throw;
34 
35 TEST(GPIOTests, Initialization)
36 {
37     // Default initialization
38     {
39         GPIO gpio;
40         EXPECT_EQ(gpio.line, 0);
41         EXPECT_FALSE(gpio.activeLow);
42     }
43 
44     // Explicit initialization
45     {
46         GPIO gpio{48, true};
47         EXPECT_EQ(gpio.line, 48);
48         EXPECT_TRUE(gpio.activeLow);
49     }
50 }
51 
52 TEST(RailTests, Constructor)
53 {
54     // Test where succeeds: No optional parameters have values
55     {
56         std::string name{"12.0V"};
57         std::optional<std::string> presence{};
58         std::optional<uint8_t> page{};
59         bool isPowerSupplyRail{true};
60         bool checkStatusVout{false};
61         bool compareVoltageToLimit{false};
62         std::optional<GPIO> gpio{};
63         Rail rail{name,
64                   presence,
65                   page,
66                   isPowerSupplyRail,
67                   checkStatusVout,
68                   compareVoltageToLimit,
69                   gpio};
70 
71         EXPECT_EQ(rail.getName(), "12.0V");
72         EXPECT_FALSE(rail.getPresence().has_value());
73         EXPECT_FALSE(rail.getPage().has_value());
74         EXPECT_TRUE(rail.isPowerSupplyRail());
75         EXPECT_FALSE(rail.getCheckStatusVout());
76         EXPECT_FALSE(rail.getCompareVoltageToLimit());
77         EXPECT_FALSE(rail.getGPIO().has_value());
78     }
79 
80     // Test where succeeds: All optional parameters have values
81     {
82         std::string name{"VCS_CPU1"};
83         std::optional<std::string> presence{
84             "/xyz/openbmc_project/inventory/system/chassis/motherboard/cpu1"};
85         std::optional<uint8_t> page{11};
86         bool isPowerSupplyRail{false};
87         bool checkStatusVout{true};
88         bool compareVoltageToLimit{true};
89         std::optional<GPIO> gpio{GPIO(60, true)};
90         Rail rail{name,
91                   presence,
92                   page,
93                   isPowerSupplyRail,
94                   checkStatusVout,
95                   compareVoltageToLimit,
96                   gpio};
97 
98         EXPECT_EQ(rail.getName(), "VCS_CPU1");
99         EXPECT_TRUE(rail.getPresence().has_value());
100         EXPECT_EQ(
101             rail.getPresence().value(),
102             "/xyz/openbmc_project/inventory/system/chassis/motherboard/cpu1");
103         EXPECT_TRUE(rail.getPage().has_value());
104         EXPECT_EQ(rail.getPage().value(), 11);
105         EXPECT_FALSE(rail.isPowerSupplyRail());
106         EXPECT_TRUE(rail.getCheckStatusVout());
107         EXPECT_TRUE(rail.getCompareVoltageToLimit());
108         EXPECT_TRUE(rail.getGPIO().has_value());
109         EXPECT_EQ(rail.getGPIO().value().line, 60);
110         EXPECT_TRUE(rail.getGPIO().value().activeLow);
111     }
112 
113     // Test where fails: checkStatusVout is true and page has no value
114     {
115         std::string name{"VDD1"};
116         std::optional<std::string> presence{};
117         std::optional<uint8_t> page{};
118         bool isPowerSupplyRail{false};
119         bool checkStatusVout{true};
120         bool compareVoltageToLimit{false};
121         std::optional<GPIO> gpio{};
122         EXPECT_THROW((Rail{name, presence, page, isPowerSupplyRail,
123                            checkStatusVout, compareVoltageToLimit, gpio}),
124                      std::invalid_argument);
125     }
126 
127     // Test where fails: compareVoltageToLimit is true and page has no value
128     {
129         std::string name{"VDD1"};
130         std::optional<std::string> presence{};
131         std::optional<uint8_t> page{};
132         bool isPowerSupplyRail{false};
133         bool checkStatusVout{false};
134         bool compareVoltageToLimit{true};
135         std::optional<GPIO> gpio{};
136         EXPECT_THROW((Rail{name, presence, page, isPowerSupplyRail,
137                            checkStatusVout, compareVoltageToLimit, gpio}),
138                      std::invalid_argument);
139     }
140 }
141 
142 TEST(RailTests, GetName)
143 {
144     std::string name{"VDD2"};
145     std::optional<std::string> presence{};
146     std::optional<uint8_t> page{};
147     bool isPowerSupplyRail{false};
148     bool checkStatusVout{false};
149     bool compareVoltageToLimit{false};
150     std::optional<GPIO> gpio{};
151     Rail rail{name,
152               presence,
153               page,
154               isPowerSupplyRail,
155               checkStatusVout,
156               compareVoltageToLimit,
157               gpio};
158 
159     EXPECT_EQ(rail.getName(), "VDD2");
160 }
161 
162 TEST(RailTests, GetPresence)
163 {
164     std::string name{"VDDR2"};
165     std::optional<uint8_t> page{};
166     bool isPowerSupplyRail{false};
167     bool checkStatusVout{false};
168     bool compareVoltageToLimit{false};
169     std::optional<GPIO> gpio{};
170 
171     // Test where presence has no value
172     {
173         std::optional<std::string> presence{};
174         Rail rail{name,
175                   presence,
176                   page,
177                   isPowerSupplyRail,
178                   checkStatusVout,
179                   compareVoltageToLimit,
180                   gpio};
181         EXPECT_FALSE(rail.getPresence().has_value());
182     }
183 
184     // Test where presence has a value
185     {
186         std::optional<std::string> presence{
187             "/xyz/openbmc_project/inventory/system/chassis/motherboard/dimm2"};
188         Rail rail{name,
189                   presence,
190                   page,
191                   isPowerSupplyRail,
192                   checkStatusVout,
193                   compareVoltageToLimit,
194                   gpio};
195         EXPECT_TRUE(rail.getPresence().has_value());
196         EXPECT_EQ(
197             rail.getPresence().value(),
198             "/xyz/openbmc_project/inventory/system/chassis/motherboard/dimm2");
199     }
200 }
201 
202 TEST(RailTests, GetPage)
203 {
204     std::string name{"VDD2"};
205     std::optional<std::string> presence{};
206     bool isPowerSupplyRail{false};
207     bool checkStatusVout{false};
208     bool compareVoltageToLimit{false};
209     std::optional<GPIO> gpio{};
210 
211     // Test where page has no value
212     {
213         std::optional<uint8_t> page{};
214         Rail rail{name,
215                   presence,
216                   page,
217                   isPowerSupplyRail,
218                   checkStatusVout,
219                   compareVoltageToLimit,
220                   gpio};
221         EXPECT_FALSE(rail.getPage().has_value());
222     }
223 
224     // Test where page has a value
225     {
226         std::optional<uint8_t> page{7};
227         Rail rail{name,
228                   presence,
229                   page,
230                   isPowerSupplyRail,
231                   checkStatusVout,
232                   compareVoltageToLimit,
233                   gpio};
234         EXPECT_TRUE(rail.getPage().has_value());
235         EXPECT_EQ(rail.getPage().value(), 7);
236     }
237 }
238 
239 TEST(RailTests, IsPowerSupplyRail)
240 {
241     std::string name{"12.0V"};
242     std::optional<std::string> presence{};
243     std::optional<uint8_t> page{};
244     bool isPowerSupplyRail{true};
245     bool checkStatusVout{false};
246     bool compareVoltageToLimit{false};
247     std::optional<GPIO> gpio{};
248     Rail rail{name,
249               presence,
250               page,
251               isPowerSupplyRail,
252               checkStatusVout,
253               compareVoltageToLimit,
254               gpio};
255 
256     EXPECT_TRUE(rail.isPowerSupplyRail());
257 }
258 
259 TEST(RailTests, GetCheckStatusVout)
260 {
261     std::string name{"VDD2"};
262     std::optional<std::string> presence{};
263     std::optional<uint8_t> page{};
264     bool isPowerSupplyRail{false};
265     bool checkStatusVout{false};
266     bool compareVoltageToLimit{false};
267     std::optional<GPIO> gpio{};
268     Rail rail{name,
269               presence,
270               page,
271               isPowerSupplyRail,
272               checkStatusVout,
273               compareVoltageToLimit,
274               gpio};
275 
276     EXPECT_FALSE(rail.getCheckStatusVout());
277 }
278 
279 TEST(RailTests, GetCompareVoltageToLimit)
280 {
281     std::string name{"VDD2"};
282     std::optional<std::string> presence{};
283     std::optional<uint8_t> page{13};
284     bool isPowerSupplyRail{false};
285     bool checkStatusVout{false};
286     bool compareVoltageToLimit{true};
287     std::optional<GPIO> gpio{};
288     Rail rail{name,
289               presence,
290               page,
291               isPowerSupplyRail,
292               checkStatusVout,
293               compareVoltageToLimit,
294               gpio};
295 
296     EXPECT_TRUE(rail.getCompareVoltageToLimit());
297 }
298 
299 TEST(RailTests, GetGPIO)
300 {
301     std::string name{"VDD2"};
302     std::optional<std::string> presence{};
303     std::optional<uint8_t> page{};
304     bool isPowerSupplyRail{false};
305     bool checkStatusVout{false};
306     bool compareVoltageToLimit{false};
307 
308     // Test where gpio has no value
309     {
310         std::optional<GPIO> gpio{};
311         Rail rail{name,
312                   presence,
313                   page,
314                   isPowerSupplyRail,
315                   checkStatusVout,
316                   compareVoltageToLimit,
317                   gpio};
318         EXPECT_FALSE(rail.getGPIO().has_value());
319     }
320 
321     // Test where gpio has a value
322     {
323         std::optional<GPIO> gpio{GPIO(12, false)};
324         Rail rail{name,
325                   presence,
326                   page,
327                   isPowerSupplyRail,
328                   checkStatusVout,
329                   compareVoltageToLimit,
330                   gpio};
331         EXPECT_TRUE(rail.getGPIO().has_value());
332         EXPECT_EQ(rail.getGPIO().value().line, 12);
333         EXPECT_FALSE(rail.getGPIO().value().activeLow);
334     }
335 }
336 
337 TEST(RailTests, IsPresent)
338 {
339     std::string name{"VDD2"};
340     std::optional<uint8_t> page{};
341     bool isPowerSupplyRail{false};
342     bool checkStatusVout{false};
343     bool compareVoltageToLimit{false};
344     std::optional<GPIO> gpio{};
345 
346     // Test where inventory path not specified; always returns true
347     {
348         std::optional<std::string> presence{};
349         Rail rail{name,
350                   presence,
351                   page,
352                   isPowerSupplyRail,
353                   checkStatusVout,
354                   compareVoltageToLimit,
355                   gpio};
356 
357         MockServices services{};
358         EXPECT_CALL(services, isPresent).Times(0);
359 
360         EXPECT_TRUE(rail.isPresent(services));
361     }
362 
363     // Test where inventory path is not present
364     {
365         std::optional<std::string> presence{
366             "/xyz/openbmc_project/inventory/system/chassis/motherboard/cpu2"};
367         Rail rail{name,
368                   presence,
369                   page,
370                   isPowerSupplyRail,
371                   checkStatusVout,
372                   compareVoltageToLimit,
373                   gpio};
374 
375         MockServices services{};
376         EXPECT_CALL(services, isPresent(*presence))
377             .Times(1)
378             .WillOnce(Return(false));
379 
380         EXPECT_FALSE(rail.isPresent(services));
381     }
382 
383     // Test where inventory path is present
384     {
385         std::optional<std::string> presence{
386             "/xyz/openbmc_project/inventory/system/chassis/motherboard/cpu2"};
387         Rail rail{name,
388                   presence,
389                   page,
390                   isPowerSupplyRail,
391                   checkStatusVout,
392                   compareVoltageToLimit,
393                   gpio};
394 
395         MockServices services{};
396         EXPECT_CALL(services, isPresent(*presence))
397             .Times(1)
398             .WillOnce(Return(true));
399 
400         EXPECT_TRUE(rail.isPresent(services));
401     }
402 
403     // Test where exception occurs trying to get presence
404     {
405         std::optional<std::string> presence{
406             "/xyz/openbmc_project/inventory/system/chassis/motherboard/cpu2"};
407         Rail rail{name,
408                   presence,
409                   page,
410                   isPowerSupplyRail,
411                   checkStatusVout,
412                   compareVoltageToLimit,
413                   gpio};
414 
415         MockServices services{};
416         EXPECT_CALL(services, isPresent(*presence))
417             .Times(1)
418             .WillOnce(Throw(std::runtime_error{"Invalid object path"}));
419 
420         try
421         {
422             rail.isPresent(services);
423             ADD_FAILURE() << "Should not have reached this line.";
424         }
425         catch (const std::exception& e)
426         {
427             EXPECT_STREQ(
428                 e.what(),
429                 "Unable to determine presence of rail VDD2 using "
430                 "inventory path "
431                 "/xyz/openbmc_project/inventory/system/chassis/motherboard/cpu2: "
432                 "Invalid object path");
433         }
434     }
435 }
436 
437 TEST(RailTests, GetStatusWord)
438 {
439     std::string name{"VDD2"};
440     std::optional<std::string> presence{};
441     bool isPowerSupplyRail{false};
442     bool checkStatusVout{false};
443     bool compareVoltageToLimit{false};
444     std::optional<GPIO> gpio{};
445 
446     // Test where page was not specified: Throws exception
447     {
448         std::optional<uint8_t> page{};
449         Rail rail{name,
450                   presence,
451                   page,
452                   isPowerSupplyRail,
453                   checkStatusVout,
454                   compareVoltageToLimit,
455                   gpio};
456 
457         MockDevice device{};
458         EXPECT_CALL(device, getStatusWord).Times(0);
459 
460         try
461         {
462             rail.getStatusWord(device);
463             ADD_FAILURE() << "Should not have reached this line.";
464         }
465         catch (const std::exception& e)
466         {
467             EXPECT_STREQ(e.what(),
468                          "Unable to read STATUS_WORD value for rail VDD2: "
469                          "No PAGE number defined for rail VDD2");
470         }
471     }
472 
473     // Test where value read successfully
474     {
475         std::optional<uint8_t> page{2};
476         Rail rail{name,
477                   presence,
478                   page,
479                   isPowerSupplyRail,
480                   checkStatusVout,
481                   compareVoltageToLimit,
482                   gpio};
483 
484         MockDevice device{};
485         EXPECT_CALL(device, getStatusWord(2)).Times(1).WillOnce(Return(0xbeef));
486 
487         EXPECT_EQ(rail.getStatusWord(device), 0xbeef);
488     }
489 
490     // Test where exception occurs trying to read value
491     {
492         std::optional<uint8_t> page{2};
493         Rail rail{name,
494                   presence,
495                   page,
496                   isPowerSupplyRail,
497                   checkStatusVout,
498                   compareVoltageToLimit,
499                   gpio};
500 
501         MockDevice device{};
502         EXPECT_CALL(device, getStatusWord(2))
503             .Times(1)
504             .WillOnce(Throw(std::runtime_error{"File does not exist"}));
505 
506         try
507         {
508             rail.getStatusWord(device);
509             ADD_FAILURE() << "Should not have reached this line.";
510         }
511         catch (const std::exception& e)
512         {
513             EXPECT_STREQ(e.what(),
514                          "Unable to read STATUS_WORD value for rail VDD2: "
515                          "File does not exist");
516         }
517     }
518 }
519 
520 TEST(RailTests, GetStatusVout)
521 {
522     std::string name{"VDD2"};
523     std::optional<std::string> presence{};
524     bool isPowerSupplyRail{false};
525     bool checkStatusVout{false};
526     bool compareVoltageToLimit{false};
527     std::optional<GPIO> gpio{};
528 
529     // Test where page was not specified: Throws exception
530     {
531         std::optional<uint8_t> page{};
532         Rail rail{name,
533                   presence,
534                   page,
535                   isPowerSupplyRail,
536                   checkStatusVout,
537                   compareVoltageToLimit,
538                   gpio};
539 
540         MockDevice device{};
541         EXPECT_CALL(device, getStatusVout).Times(0);
542 
543         try
544         {
545             rail.getStatusVout(device);
546             ADD_FAILURE() << "Should not have reached this line.";
547         }
548         catch (const std::exception& e)
549         {
550             EXPECT_STREQ(e.what(),
551                          "Unable to read STATUS_VOUT value for rail VDD2: "
552                          "No PAGE number defined for rail VDD2");
553         }
554     }
555 
556     // Test where value read successfully
557     {
558         std::optional<uint8_t> page{2};
559         Rail rail{name,
560                   presence,
561                   page,
562                   isPowerSupplyRail,
563                   checkStatusVout,
564                   compareVoltageToLimit,
565                   gpio};
566 
567         MockDevice device{};
568         EXPECT_CALL(device, getStatusVout(2)).Times(1).WillOnce(Return(0xad));
569 
570         EXPECT_EQ(rail.getStatusVout(device), 0xad);
571     }
572 
573     // Test where exception occurs trying to read value
574     {
575         std::optional<uint8_t> page{2};
576         Rail rail{name,
577                   presence,
578                   page,
579                   isPowerSupplyRail,
580                   checkStatusVout,
581                   compareVoltageToLimit,
582                   gpio};
583 
584         MockDevice device{};
585         EXPECT_CALL(device, getStatusVout(2))
586             .Times(1)
587             .WillOnce(Throw(std::runtime_error{"File does not exist"}));
588 
589         try
590         {
591             rail.getStatusVout(device);
592             ADD_FAILURE() << "Should not have reached this line.";
593         }
594         catch (const std::exception& e)
595         {
596             EXPECT_STREQ(e.what(),
597                          "Unable to read STATUS_VOUT value for rail VDD2: "
598                          "File does not exist");
599         }
600     }
601 }
602 
603 TEST(RailTests, GetReadVout)
604 {
605     std::string name{"VDD2"};
606     std::optional<std::string> presence{};
607     bool isPowerSupplyRail{false};
608     bool checkStatusVout{false};
609     bool compareVoltageToLimit{false};
610     std::optional<GPIO> gpio{};
611 
612     // Test where page was not specified: Throws exception
613     {
614         std::optional<uint8_t> page{};
615         Rail rail{name,
616                   presence,
617                   page,
618                   isPowerSupplyRail,
619                   checkStatusVout,
620                   compareVoltageToLimit,
621                   gpio};
622 
623         MockDevice device{};
624         EXPECT_CALL(device, getReadVout).Times(0);
625 
626         try
627         {
628             rail.getReadVout(device);
629             ADD_FAILURE() << "Should not have reached this line.";
630         }
631         catch (const std::exception& e)
632         {
633             EXPECT_STREQ(e.what(),
634                          "Unable to read READ_VOUT value for rail VDD2: "
635                          "No PAGE number defined for rail VDD2");
636         }
637     }
638 
639     // Test where value read successfully
640     {
641         std::optional<uint8_t> page{2};
642         Rail rail{name,
643                   presence,
644                   page,
645                   isPowerSupplyRail,
646                   checkStatusVout,
647                   compareVoltageToLimit,
648                   gpio};
649 
650         MockDevice device{};
651         EXPECT_CALL(device, getReadVout(2)).Times(1).WillOnce(Return(1.23));
652 
653         EXPECT_EQ(rail.getReadVout(device), 1.23);
654     }
655 
656     // Test where exception occurs trying to read value
657     {
658         std::optional<uint8_t> page{2};
659         Rail rail{name,
660                   presence,
661                   page,
662                   isPowerSupplyRail,
663                   checkStatusVout,
664                   compareVoltageToLimit,
665                   gpio};
666 
667         MockDevice device{};
668         EXPECT_CALL(device, getReadVout(2))
669             .Times(1)
670             .WillOnce(Throw(std::runtime_error{"File does not exist"}));
671 
672         try
673         {
674             rail.getReadVout(device);
675             ADD_FAILURE() << "Should not have reached this line.";
676         }
677         catch (const std::exception& e)
678         {
679             EXPECT_STREQ(e.what(),
680                          "Unable to read READ_VOUT value for rail VDD2: "
681                          "File does not exist");
682         }
683     }
684 }
685 
686 TEST(RailTests, GetVoutUVFaultLimit)
687 {
688     std::string name{"VDD2"};
689     std::optional<std::string> presence{};
690     bool isPowerSupplyRail{false};
691     bool checkStatusVout{false};
692     bool compareVoltageToLimit{false};
693     std::optional<GPIO> gpio{};
694 
695     // Test where page was not specified: Throws exception
696     {
697         std::optional<uint8_t> page{};
698         Rail rail{name,
699                   presence,
700                   page,
701                   isPowerSupplyRail,
702                   checkStatusVout,
703                   compareVoltageToLimit,
704                   gpio};
705 
706         MockDevice device{};
707         EXPECT_CALL(device, getVoutUVFaultLimit).Times(0);
708 
709         try
710         {
711             rail.getVoutUVFaultLimit(device);
712             ADD_FAILURE() << "Should not have reached this line.";
713         }
714         catch (const std::exception& e)
715         {
716             EXPECT_STREQ(
717                 e.what(),
718                 "Unable to read VOUT_UV_FAULT_LIMIT value for rail VDD2: "
719                 "No PAGE number defined for rail VDD2");
720         }
721     }
722 
723     // Test where value read successfully
724     {
725         std::optional<uint8_t> page{2};
726         Rail rail{name,
727                   presence,
728                   page,
729                   isPowerSupplyRail,
730                   checkStatusVout,
731                   compareVoltageToLimit,
732                   gpio};
733 
734         MockDevice device{};
735         EXPECT_CALL(device, getVoutUVFaultLimit(2))
736             .Times(1)
737             .WillOnce(Return(0.9));
738 
739         EXPECT_EQ(rail.getVoutUVFaultLimit(device), 0.9);
740     }
741 
742     // Test where exception occurs trying to read value
743     {
744         std::optional<uint8_t> page{2};
745         Rail rail{name,
746                   presence,
747                   page,
748                   isPowerSupplyRail,
749                   checkStatusVout,
750                   compareVoltageToLimit,
751                   gpio};
752 
753         MockDevice device{};
754         EXPECT_CALL(device, getVoutUVFaultLimit(2))
755             .Times(1)
756             .WillOnce(Throw(std::runtime_error{"File does not exist"}));
757 
758         try
759         {
760             rail.getVoutUVFaultLimit(device);
761             ADD_FAILURE() << "Should not have reached this line.";
762         }
763         catch (const std::exception& e)
764         {
765             EXPECT_STREQ(
766                 e.what(),
767                 "Unable to read VOUT_UV_FAULT_LIMIT value for rail VDD2: "
768                 "File does not exist");
769         }
770     }
771 }
772 
773 TEST(RailTests, HasPgoodFault)
774 {
775     std::string name{"VDD2"};
776     bool isPowerSupplyRail{false};
777 
778     // Test where presence check defined: Rail is not present
779     {
780         std::optional<std::string> presence{
781             "/xyz/openbmc_project/inventory/system/chassis/motherboard/cpu2"};
782         std::optional<uint8_t> page{3};
783         bool checkStatusVout{true};
784         bool compareVoltageToLimit{false};
785         std::optional<GPIO> gpio{};
786         Rail rail{name,
787                   presence,
788                   page,
789                   isPowerSupplyRail,
790                   checkStatusVout,
791                   compareVoltageToLimit,
792                   gpio};
793 
794         MockDevice device{};
795 
796         MockServices services{};
797         EXPECT_CALL(services, isPresent(*presence))
798             .Times(1)
799             .WillOnce(Return(false));
800         EXPECT_CALL(services, logInfoMsg("Rail VDD2 is not present")).Times(1);
801 
802         std::vector<int> gpioValues{};
803         std::map<std::string, std::string> additionalData{};
804         EXPECT_FALSE(
805             rail.hasPgoodFault(device, services, gpioValues, additionalData));
806         EXPECT_EQ(additionalData.size(), 0);
807     }
808 
809     // Test where presence check defined: Rail is present
810     {
811         std::optional<std::string> presence{
812             "/xyz/openbmc_project/inventory/system/chassis/motherboard/cpu2"};
813         std::optional<uint8_t> page{};
814         bool checkStatusVout{false};
815         bool compareVoltageToLimit{false};
816         std::optional<GPIO> gpio{};
817         Rail rail{name,
818                   presence,
819                   page,
820                   isPowerSupplyRail,
821                   checkStatusVout,
822                   compareVoltageToLimit,
823                   gpio};
824 
825         MockDevice device{};
826 
827         MockServices services{};
828         EXPECT_CALL(services, isPresent(*presence))
829             .Times(1)
830             .WillOnce(Return(true));
831         EXPECT_CALL(services, logInfoMsg).Times(0);
832 
833         std::vector<int> gpioValues{};
834         std::map<std::string, std::string> additionalData{};
835         EXPECT_FALSE(
836             rail.hasPgoodFault(device, services, gpioValues, additionalData));
837         EXPECT_EQ(additionalData.size(), 0);
838     }
839 
840     // Test where no checks are specified
841     {
842         std::optional<std::string> presence{};
843         std::optional<uint8_t> page{};
844         bool checkStatusVout{false};
845         bool compareVoltageToLimit{false};
846         std::optional<GPIO> gpio{};
847         Rail rail{name,
848                   presence,
849                   page,
850                   isPowerSupplyRail,
851                   checkStatusVout,
852                   compareVoltageToLimit,
853                   gpio};
854 
855         MockDevice device{};
856 
857         MockServices services{};
858 
859         std::vector<int> gpioValues{};
860         std::map<std::string, std::string> additionalData{};
861         EXPECT_FALSE(
862             rail.hasPgoodFault(device, services, gpioValues, additionalData));
863         EXPECT_EQ(additionalData.size(), 0);
864     }
865 
866     // Test where 1 check defined: STATUS_VOUT: No fault detected
867     {
868         std::optional<std::string> presence{};
869         std::optional<uint8_t> page{2};
870         bool checkStatusVout{true};
871         bool compareVoltageToLimit{false};
872         std::optional<GPIO> gpio{};
873         Rail rail{name,
874                   presence,
875                   page,
876                   isPowerSupplyRail,
877                   checkStatusVout,
878                   compareVoltageToLimit,
879                   gpio};
880 
881         MockDevice device{};
882         EXPECT_CALL(device, getStatusVout(2)).Times(1).WillOnce(Return(0x00));
883 
884         MockServices services{};
885 
886         std::vector<int> gpioValues{};
887         std::map<std::string, std::string> additionalData{};
888         EXPECT_FALSE(
889             rail.hasPgoodFault(device, services, gpioValues, additionalData));
890         EXPECT_EQ(additionalData.size(), 0);
891     }
892 
893     // Test where 1 check defined: STATUS_VOUT: No fault detected, but warning
894     // bits set
895     {
896         std::optional<std::string> presence{};
897         std::optional<uint8_t> page{2};
898         bool checkStatusVout{true};
899         bool compareVoltageToLimit{false};
900         std::optional<GPIO> gpio{};
901         Rail rail{name,
902                   presence,
903                   page,
904                   isPowerSupplyRail,
905                   checkStatusVout,
906                   compareVoltageToLimit,
907                   gpio};
908 
909         MockDevice device{};
910         EXPECT_CALL(device, getStatusVout(2)).Times(1).WillOnce(Return(0x6a));
911 
912         MockServices services{};
913         EXPECT_CALL(
914             services,
915             logInfoMsg("Rail VDD2 has warning bits set in STATUS_VOUT: 0x6a"))
916             .Times(1);
917 
918         std::vector<int> gpioValues{};
919         std::map<std::string, std::string> additionalData{};
920         EXPECT_FALSE(
921             rail.hasPgoodFault(device, services, gpioValues, additionalData));
922         EXPECT_EQ(additionalData.size(), 0);
923     }
924 
925     // Test where 1 check defined: STATUS_VOUT: Fault detected
926     // STATUS_WORD captured in additional data
927     {
928         std::optional<std::string> presence{};
929         std::optional<uint8_t> page{2};
930         bool checkStatusVout{true};
931         bool compareVoltageToLimit{false};
932         std::optional<GPIO> gpio{};
933         Rail rail{name,
934                   presence,
935                   page,
936                   isPowerSupplyRail,
937                   checkStatusVout,
938                   compareVoltageToLimit,
939                   gpio};
940 
941         MockDevice device{};
942         EXPECT_CALL(device, getStatusVout(2)).Times(1).WillOnce(Return(0x10));
943         EXPECT_CALL(device, getStatusWord(2)).Times(1).WillOnce(Return(0xbeef));
944 
945         MockServices services{};
946         EXPECT_CALL(services, logInfoMsg("Rail VDD2 STATUS_WORD: 0xbeef"))
947             .Times(1);
948         EXPECT_CALL(services, logErrorMsg("Pgood fault detected in rail VDD2"))
949             .Times(1);
950         EXPECT_CALL(
951             services,
952             logErrorMsg("Rail VDD2 has fault bits set in STATUS_VOUT: 0x10"))
953             .Times(1);
954 
955         std::vector<int> gpioValues{};
956         std::map<std::string, std::string> additionalData{};
957         EXPECT_TRUE(
958             rail.hasPgoodFault(device, services, gpioValues, additionalData));
959         EXPECT_EQ(additionalData.size(), 3);
960         EXPECT_EQ(additionalData["RAIL_NAME"], "VDD2");
961         EXPECT_EQ(additionalData["STATUS_VOUT"], "0x10");
962         EXPECT_EQ(additionalData["STATUS_WORD"], "0xbeef");
963     }
964 
965     // Test where 1 check defined: STATUS_VOUT: Exception thrown
966     {
967         std::optional<std::string> presence{};
968         std::optional<uint8_t> page{2};
969         bool checkStatusVout{true};
970         bool compareVoltageToLimit{false};
971         std::optional<GPIO> gpio{};
972         Rail rail{name,
973                   presence,
974                   page,
975                   isPowerSupplyRail,
976                   checkStatusVout,
977                   compareVoltageToLimit,
978                   gpio};
979 
980         MockDevice device{};
981         EXPECT_CALL(device, getStatusVout(2))
982             .Times(1)
983             .WillOnce(Throw(std::runtime_error{"File does not exist"}));
984 
985         MockServices services{};
986 
987         std::vector<int> gpioValues{};
988         std::map<std::string, std::string> additionalData{};
989         try
990         {
991             rail.hasPgoodFault(device, services, gpioValues, additionalData);
992             ADD_FAILURE() << "Should not have reached this line.";
993         }
994         catch (const std::exception& e)
995         {
996             EXPECT_STREQ(e.what(),
997                          "Unable to read STATUS_VOUT value for rail VDD2: "
998                          "File does not exist");
999         }
1000     }
1001 
1002     // Test where 1 check defined: GPIO: No fault detected
1003     // GPIO value is 1 and GPIO is active high
1004     {
1005         std::optional<std::string> presence{};
1006         std::optional<uint8_t> page{};
1007         bool checkStatusVout{false};
1008         bool compareVoltageToLimit{false};
1009         bool activeLow{false};
1010         std::optional<GPIO> gpio{GPIO(3, activeLow)};
1011         Rail rail{name,
1012                   presence,
1013                   page,
1014                   isPowerSupplyRail,
1015                   checkStatusVout,
1016                   compareVoltageToLimit,
1017                   gpio};
1018 
1019         MockDevice device{};
1020 
1021         MockServices services{};
1022 
1023         std::vector<int> gpioValues{1, 1, 1, 1, 1, 1};
1024         std::map<std::string, std::string> additionalData{};
1025         EXPECT_FALSE(
1026             rail.hasPgoodFault(device, services, gpioValues, additionalData));
1027         EXPECT_EQ(additionalData.size(), 0);
1028     }
1029 
1030     // Test where 1 check defined: GPIO: Fault detected
1031     // GPIO value is 0 and GPIO is active high
1032     // STATUS_WORD not captured since no PMBus page defined
1033     {
1034         std::optional<std::string> presence{};
1035         std::optional<uint8_t> page{};
1036         bool checkStatusVout{false};
1037         bool compareVoltageToLimit{false};
1038         bool activeLow{false};
1039         std::optional<GPIO> gpio{GPIO(3, activeLow)};
1040         Rail rail{name,
1041                   presence,
1042                   page,
1043                   isPowerSupplyRail,
1044                   checkStatusVout,
1045                   compareVoltageToLimit,
1046                   gpio};
1047 
1048         MockDevice device{};
1049 
1050         MockServices services{};
1051         EXPECT_CALL(services, logErrorMsg("Pgood fault detected in rail VDD2"))
1052             .Times(1);
1053         EXPECT_CALL(
1054             services,
1055             logErrorMsg(
1056                 "Rail VDD2 pgood GPIO line offset 3 has inactive value 0"))
1057             .Times(1);
1058 
1059         std::vector<int> gpioValues{1, 1, 1, 0, 1, 1};
1060         std::map<std::string, std::string> additionalData{};
1061         EXPECT_TRUE(
1062             rail.hasPgoodFault(device, services, gpioValues, additionalData));
1063         EXPECT_EQ(additionalData.size(), 3);
1064         EXPECT_EQ(additionalData["RAIL_NAME"], "VDD2");
1065         EXPECT_EQ(additionalData["GPIO_LINE"], "3");
1066         EXPECT_EQ(additionalData["GPIO_VALUE"], "0");
1067     }
1068 
1069     // Test where 1 check defined: GPIO: Exception thrown: Invalid line offset
1070     {
1071         std::optional<std::string> presence{};
1072         std::optional<uint8_t> page{};
1073         bool checkStatusVout{false};
1074         bool compareVoltageToLimit{false};
1075         bool activeLow{false};
1076         std::optional<GPIO> gpio{GPIO(6, activeLow)};
1077         Rail rail{name,
1078                   presence,
1079                   page,
1080                   isPowerSupplyRail,
1081                   checkStatusVout,
1082                   compareVoltageToLimit,
1083                   gpio};
1084 
1085         MockDevice device{};
1086 
1087         MockServices services{};
1088 
1089         std::vector<int> gpioValues{1, 1, 1, 1, 1, 1};
1090         std::map<std::string, std::string> additionalData{};
1091         try
1092         {
1093             rail.hasPgoodFault(device, services, gpioValues, additionalData);
1094             ADD_FAILURE() << "Should not have reached this line.";
1095         }
1096         catch (const std::exception& e)
1097         {
1098             EXPECT_STREQ(e.what(), "Invalid GPIO line offset 6 for rail VDD2: "
1099                                    "Device only has 6 GPIO values");
1100         }
1101     }
1102 
1103     // Test where 1 check defined: READ_VOUT: No fault detected
1104     // Output voltage > UV limit
1105     {
1106         std::optional<std::string> presence{};
1107         std::optional<uint8_t> page{2};
1108         bool checkStatusVout{false};
1109         bool compareVoltageToLimit{true};
1110         std::optional<GPIO> gpio{};
1111         Rail rail{name,
1112                   presence,
1113                   page,
1114                   isPowerSupplyRail,
1115                   checkStatusVout,
1116                   compareVoltageToLimit,
1117                   gpio};
1118 
1119         MockDevice device{};
1120         EXPECT_CALL(device, getReadVout(2)).Times(1).WillOnce(Return(1.1));
1121         EXPECT_CALL(device, getVoutUVFaultLimit(2))
1122             .Times(1)
1123             .WillOnce(Return(1.0));
1124 
1125         MockServices services{};
1126 
1127         std::vector<int> gpioValues{};
1128         std::map<std::string, std::string> additionalData{};
1129         EXPECT_FALSE(
1130             rail.hasPgoodFault(device, services, gpioValues, additionalData));
1131         EXPECT_EQ(additionalData.size(), 0);
1132     }
1133 
1134     // Test where 1 check defined: READ_VOUT: Fault detected
1135     // Output voltage < UV limit
1136     {
1137         std::optional<std::string> presence{};
1138         std::optional<uint8_t> page{2};
1139         bool checkStatusVout{false};
1140         bool compareVoltageToLimit{true};
1141         std::optional<GPIO> gpio{};
1142         Rail rail{name,
1143                   presence,
1144                   page,
1145                   isPowerSupplyRail,
1146                   checkStatusVout,
1147                   compareVoltageToLimit,
1148                   gpio};
1149 
1150         MockDevice device{};
1151         EXPECT_CALL(device, getReadVout(2)).Times(1).WillOnce(Return(1.1));
1152         EXPECT_CALL(device, getVoutUVFaultLimit(2))
1153             .Times(1)
1154             .WillOnce(Return(1.2));
1155         EXPECT_CALL(device, getStatusWord(2)).Times(1).WillOnce(Return(0xbeef));
1156 
1157         MockServices services{};
1158         EXPECT_CALL(services, logInfoMsg("Rail VDD2 STATUS_WORD: 0xbeef"))
1159             .Times(1);
1160         EXPECT_CALL(services, logErrorMsg("Pgood fault detected in rail VDD2"))
1161             .Times(1);
1162         EXPECT_CALL(
1163             services,
1164             logErrorMsg(
1165                 "Rail VDD2 output voltage 1.1V is <= UV fault limit 1.2V"))
1166             .Times(1);
1167 
1168         std::vector<int> gpioValues{};
1169         std::map<std::string, std::string> additionalData{};
1170         EXPECT_TRUE(
1171             rail.hasPgoodFault(device, services, gpioValues, additionalData));
1172         EXPECT_EQ(additionalData.size(), 4);
1173         EXPECT_EQ(additionalData["RAIL_NAME"], "VDD2");
1174         EXPECT_EQ(additionalData["READ_VOUT"], "1.1");
1175         EXPECT_EQ(additionalData["VOUT_UV_FAULT_LIMIT"], "1.2");
1176         EXPECT_EQ(additionalData["STATUS_WORD"], "0xbeef");
1177     }
1178 
1179     // Test where 1 check defined: READ_VOUT: Exception thrown
1180     {
1181         std::optional<std::string> presence{};
1182         std::optional<uint8_t> page{2};
1183         bool checkStatusVout{false};
1184         bool compareVoltageToLimit{true};
1185         std::optional<GPIO> gpio{};
1186         Rail rail{name,
1187                   presence,
1188                   page,
1189                   isPowerSupplyRail,
1190                   checkStatusVout,
1191                   compareVoltageToLimit,
1192                   gpio};
1193 
1194         MockDevice device{};
1195         EXPECT_CALL(device, getReadVout(2))
1196             .Times(1)
1197             .WillOnce(Throw(std::runtime_error{"File does not exist"}));
1198 
1199         MockServices services{};
1200 
1201         std::vector<int> gpioValues{};
1202         std::map<std::string, std::string> additionalData{};
1203         try
1204         {
1205             rail.hasPgoodFault(device, services, gpioValues, additionalData);
1206             ADD_FAILURE() << "Should not have reached this line.";
1207         }
1208         catch (const std::exception& e)
1209         {
1210             EXPECT_STREQ(e.what(),
1211                          "Unable to read READ_VOUT value for rail VDD2: "
1212                          "File does not exist");
1213         }
1214     }
1215 
1216     // Test where 3 checks defined: No fault detected
1217     // GPIO value is 0 and GPIO is active low
1218     {
1219         std::optional<std::string> presence{};
1220         std::optional<uint8_t> page{2};
1221         bool checkStatusVout{true};
1222         bool compareVoltageToLimit{true};
1223         bool activeLow{true};
1224         std::optional<GPIO> gpio{GPIO(3, activeLow)};
1225         Rail rail{name,
1226                   presence,
1227                   page,
1228                   isPowerSupplyRail,
1229                   checkStatusVout,
1230                   compareVoltageToLimit,
1231                   gpio};
1232 
1233         MockDevice device{};
1234         EXPECT_CALL(device, getStatusVout(2)).Times(1).WillOnce(Return(0x00));
1235         EXPECT_CALL(device, getReadVout(2)).Times(1).WillOnce(Return(1.1));
1236         EXPECT_CALL(device, getVoutUVFaultLimit(2))
1237             .Times(1)
1238             .WillOnce(Return(0.9));
1239 
1240         MockServices services{};
1241 
1242         std::vector<int> gpioValues{0, 0, 0, 0, 0, 0};
1243         std::map<std::string, std::string> additionalData{};
1244         EXPECT_FALSE(
1245             rail.hasPgoodFault(device, services, gpioValues, additionalData));
1246         EXPECT_EQ(additionalData.size(), 0);
1247     }
1248 
1249     // Test where 3 checks defined: Fault detected via STATUS_VOUT
1250     {
1251         std::optional<std::string> presence{};
1252         std::optional<uint8_t> page{2};
1253         bool checkStatusVout{true};
1254         bool compareVoltageToLimit{true};
1255         bool activeLow{true};
1256         std::optional<GPIO> gpio{GPIO(3, activeLow)};
1257         Rail rail{name,
1258                   presence,
1259                   page,
1260                   isPowerSupplyRail,
1261                   checkStatusVout,
1262                   compareVoltageToLimit,
1263                   gpio};
1264 
1265         MockDevice device{};
1266         EXPECT_CALL(device, getStatusVout(2)).Times(1).WillOnce(Return(0x10));
1267         EXPECT_CALL(device, getStatusWord(2)).Times(1).WillOnce(Return(0xbeef));
1268 
1269         MockServices services{};
1270         EXPECT_CALL(services, logInfoMsg("Rail VDD2 STATUS_WORD: 0xbeef"))
1271             .Times(1);
1272         EXPECT_CALL(services, logErrorMsg("Pgood fault detected in rail VDD2"))
1273             .Times(1);
1274         EXPECT_CALL(
1275             services,
1276             logErrorMsg("Rail VDD2 has fault bits set in STATUS_VOUT: 0x10"))
1277             .Times(1);
1278 
1279         std::vector<int> gpioValues{0, 0, 0, 0, 0, 0};
1280         std::map<std::string, std::string> additionalData{};
1281         EXPECT_TRUE(
1282             rail.hasPgoodFault(device, services, gpioValues, additionalData));
1283         EXPECT_EQ(additionalData.size(), 3);
1284         EXPECT_EQ(additionalData["RAIL_NAME"], "VDD2");
1285         EXPECT_EQ(additionalData["STATUS_VOUT"], "0x10");
1286         EXPECT_EQ(additionalData["STATUS_WORD"], "0xbeef");
1287     }
1288 
1289     // Test where 3 checks defined: Fault detected via GPIO
1290     // GPIO value is 1 and GPIO is active low
1291     {
1292         std::optional<std::string> presence{};
1293         std::optional<uint8_t> page{2};
1294         bool checkStatusVout{true};
1295         bool compareVoltageToLimit{true};
1296         bool activeLow{true};
1297         std::optional<GPIO> gpio{GPIO(3, activeLow)};
1298         Rail rail{name,
1299                   presence,
1300                   page,
1301                   isPowerSupplyRail,
1302                   checkStatusVout,
1303                   compareVoltageToLimit,
1304                   gpio};
1305 
1306         MockDevice device{};
1307         EXPECT_CALL(device, getStatusVout(2)).Times(1).WillOnce(Return(0x00));
1308         EXPECT_CALL(device, getStatusWord(2)).Times(1).WillOnce(Return(0xbeef));
1309 
1310         MockServices services{};
1311         EXPECT_CALL(services, logInfoMsg("Rail VDD2 STATUS_WORD: 0xbeef"))
1312             .Times(1);
1313         EXPECT_CALL(services, logErrorMsg("Pgood fault detected in rail VDD2"))
1314             .Times(1);
1315         EXPECT_CALL(
1316             services,
1317             logErrorMsg(
1318                 "Rail VDD2 pgood GPIO line offset 3 has inactive value 1"))
1319             .Times(1);
1320 
1321         std::vector<int> gpioValues{0, 0, 0, 1, 0, 0};
1322         std::map<std::string, std::string> additionalData{};
1323         EXPECT_TRUE(
1324             rail.hasPgoodFault(device, services, gpioValues, additionalData));
1325         EXPECT_EQ(additionalData.size(), 4);
1326         EXPECT_EQ(additionalData["RAIL_NAME"], "VDD2");
1327         EXPECT_EQ(additionalData["GPIO_LINE"], "3");
1328         EXPECT_EQ(additionalData["GPIO_VALUE"], "1");
1329         EXPECT_EQ(additionalData["STATUS_WORD"], "0xbeef");
1330     }
1331 
1332     // Test where 3 checks defined: Fault detected via READ_VOUT
1333     // Output voltage == UV limit
1334     // STATUS_WORD not captured because reading it caused an exception
1335     {
1336         std::optional<std::string> presence{};
1337         std::optional<uint8_t> page{2};
1338         bool checkStatusVout{true};
1339         bool compareVoltageToLimit{true};
1340         bool activeLow{true};
1341         std::optional<GPIO> gpio{GPIO(3, activeLow)};
1342         Rail rail{name,
1343                   presence,
1344                   page,
1345                   isPowerSupplyRail,
1346                   checkStatusVout,
1347                   compareVoltageToLimit,
1348                   gpio};
1349 
1350         MockDevice device{};
1351         EXPECT_CALL(device, getStatusVout(2)).Times(1).WillOnce(Return(0x00));
1352         EXPECT_CALL(device, getReadVout(2)).Times(1).WillOnce(Return(1.1));
1353         EXPECT_CALL(device, getVoutUVFaultLimit(2))
1354             .Times(1)
1355             .WillOnce(Return(1.1));
1356         EXPECT_CALL(device, getStatusWord(2))
1357             .Times(1)
1358             .WillOnce(Throw(std::runtime_error{"File does not exist"}));
1359 
1360         MockServices services{};
1361         EXPECT_CALL(services, logErrorMsg("Pgood fault detected in rail VDD2"))
1362             .Times(1);
1363         EXPECT_CALL(
1364             services,
1365             logErrorMsg(
1366                 "Rail VDD2 output voltage 1.1V is <= UV fault limit 1.1V"))
1367             .Times(1);
1368 
1369         std::vector<int> gpioValues{0, 0, 0, 0, 0, 0};
1370         std::map<std::string, std::string> additionalData{};
1371         EXPECT_TRUE(
1372             rail.hasPgoodFault(device, services, gpioValues, additionalData));
1373         EXPECT_EQ(additionalData.size(), 3);
1374         EXPECT_EQ(additionalData["RAIL_NAME"], "VDD2");
1375         EXPECT_EQ(additionalData["READ_VOUT"], "1.1");
1376         EXPECT_EQ(additionalData["VOUT_UV_FAULT_LIMIT"], "1.1");
1377     }
1378 }
1379