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_pmbus.hpp"
18 #include "mock_services.hpp"
19 #include "pmbus.hpp"
20 #include "pmbus_driver_device.hpp"
21 #include "rail.hpp"
22 #include "services.hpp"
23 #include "temporary_subdirectory.hpp"
24 
25 #include <cstdint>
26 #include <exception>
27 #include <filesystem>
28 #include <format>
29 #include <fstream>
30 #include <map>
31 #include <memory>
32 #include <optional>
33 #include <stdexcept>
34 #include <string>
35 #include <utility>
36 #include <vector>
37 
38 #include <gmock/gmock.h>
39 #include <gtest/gtest.h>
40 
41 using namespace phosphor::power::sequencer;
42 using namespace phosphor::pmbus;
43 using namespace phosphor::power::util;
44 namespace fs = std::filesystem;
45 
46 using ::testing::Return;
47 using ::testing::Throw;
48 
49 class PMBusDriverDeviceTests : public ::testing::Test
50 {
51   protected:
52     /**
53      * Constructor.
54      *
55      * Creates a temporary directory to use in simulating sysfs files.
56      */
57     PMBusDriverDeviceTests() : ::testing::Test{}
58     {
59         tempDirPath = tempDir.getPath();
60     }
61 
62     /**
63      * Creates a Rail object that checks for a pgood fault using STATUS_VOUT.
64      *
65      * @param name Unique name for the rail
66      * @param pageNum PMBus PAGE number of the rail
67      * @return Rail object
68      */
69     std::unique_ptr<Rail> createRail(const std::string& name, uint8_t pageNum)
70     {
71         std::optional<std::string> presence{};
72         std::optional<uint8_t> page{pageNum};
73         bool isPowerSupplyRail{false};
74         bool checkStatusVout{true};
75         bool compareVoltageToLimit{false};
76         std::optional<GPIO> gpio{};
77         return std::make_unique<Rail>(name, presence, page, isPowerSupplyRail,
78                                       checkStatusVout, compareVoltageToLimit,
79                                       gpio);
80     }
81 
82     /**
83      * Creates a file with the specified contents within the temporary
84      * directory.
85      *
86      * @param name File name
87      * @param contents File contents
88      */
89     void createFile(const std::string& name, const std::string& contents = "")
90     {
91         fs::path path{tempDirPath / name};
92         std::ofstream out{path};
93         out << contents;
94         out.close();
95     }
96 
97     /**
98      * Temporary subdirectory used to create simulated sysfs / hmmon files.
99      */
100     TemporarySubDirectory tempDir;
101 
102     /**
103      * Path to temporary subdirectory.
104      */
105     fs::path tempDirPath;
106 };
107 
108 TEST_F(PMBusDriverDeviceTests, Constructor)
109 {
110     // Test where works; optional parameters not specified
111     {
112         MockServices services;
113 
114         std::string name{"XYZ_PSEQ"};
115         std::vector<std::unique_ptr<Rail>> rails;
116         rails.emplace_back(createRail("VDD", 5));
117         rails.emplace_back(createRail("VIO", 7));
118         uint8_t bus{3};
119         uint16_t address{0x72};
120         PMBusDriverDevice device{name, std::move(rails), services, bus,
121                                  address};
122 
123         EXPECT_EQ(device.getName(), name);
124         EXPECT_EQ(device.getRails().size(), 2);
125         EXPECT_EQ(device.getRails()[0]->getName(), "VDD");
126         EXPECT_EQ(device.getRails()[1]->getName(), "VIO");
127         EXPECT_EQ(device.getBus(), bus);
128         EXPECT_EQ(device.getAddress(), address);
129         EXPECT_EQ(device.getDriverName(), "");
130         EXPECT_EQ(device.getInstance(), 0);
131         EXPECT_NE(&(device.getPMBusInterface()), nullptr);
132     }
133 
134     // Test where works; optional parameters specified
135     {
136         MockServices services;
137 
138         std::string name{"XYZ_PSEQ"};
139         std::vector<std::unique_ptr<Rail>> rails;
140         rails.emplace_back(createRail("VDD", 5));
141         rails.emplace_back(createRail("VIO", 7));
142         uint8_t bus{3};
143         uint16_t address{0x72};
144         std::string driverName{"xyzdev"};
145         size_t instance{3};
146         PMBusDriverDevice device{name,    std::move(rails), services, bus,
147                                  address, driverName,       instance};
148 
149         EXPECT_EQ(device.getName(), name);
150         EXPECT_EQ(device.getRails().size(), 2);
151         EXPECT_EQ(device.getRails()[0]->getName(), "VDD");
152         EXPECT_EQ(device.getRails()[1]->getName(), "VIO");
153         EXPECT_EQ(device.getBus(), bus);
154         EXPECT_EQ(device.getAddress(), address);
155         EXPECT_EQ(device.getDriverName(), driverName);
156         EXPECT_EQ(device.getInstance(), instance);
157         EXPECT_NE(&(device.getPMBusInterface()), nullptr);
158     }
159 }
160 
161 TEST_F(PMBusDriverDeviceTests, GetBus)
162 {
163     MockServices services;
164 
165     std::string name{"XYZ_PSEQ"};
166     std::vector<std::unique_ptr<Rail>> rails;
167     uint8_t bus{4};
168     uint16_t address{0x72};
169     PMBusDriverDevice device{name, std::move(rails), services, bus, address};
170 
171     EXPECT_EQ(device.getBus(), bus);
172 }
173 
174 TEST_F(PMBusDriverDeviceTests, GetAddress)
175 {
176     MockServices services;
177 
178     std::string name{"XYZ_PSEQ"};
179     std::vector<std::unique_ptr<Rail>> rails;
180     uint8_t bus{3};
181     uint16_t address{0xab};
182     PMBusDriverDevice device{name, std::move(rails), services, bus, address};
183 
184     EXPECT_EQ(device.getAddress(), address);
185 }
186 
187 TEST_F(PMBusDriverDeviceTests, GetDriverName)
188 {
189     MockServices services;
190 
191     std::string name{"XYZ_PSEQ"};
192     std::vector<std::unique_ptr<Rail>> rails;
193     uint8_t bus{3};
194     uint16_t address{0x72};
195     std::string driverName{"xyzdev"};
196     PMBusDriverDevice device{name, std::move(rails), services,
197                              bus,  address,          driverName};
198 
199     EXPECT_EQ(device.getDriverName(), driverName);
200 }
201 
202 TEST_F(PMBusDriverDeviceTests, GetInstance)
203 {
204     MockServices services;
205 
206     std::string name{"XYZ_PSEQ"};
207     std::vector<std::unique_ptr<Rail>> rails;
208     uint8_t bus{3};
209     uint16_t address{0x72};
210     std::string driverName{"xyzdev"};
211     size_t instance{3};
212     PMBusDriverDevice device{name,    std::move(rails), services, bus,
213                              address, driverName,       instance};
214 
215     EXPECT_EQ(device.getInstance(), instance);
216 }
217 
218 TEST_F(PMBusDriverDeviceTests, GetPMBusInterface)
219 {
220     MockServices services;
221 
222     std::string name{"XYZ_PSEQ"};
223     std::vector<std::unique_ptr<Rail>> rails;
224     uint8_t bus{3};
225     uint16_t address{0x72};
226     PMBusDriverDevice device{name, std::move(rails), services, bus, address};
227 
228     EXPECT_NE(&(device.getPMBusInterface()), nullptr);
229 }
230 
231 TEST_F(PMBusDriverDeviceTests, GetGPIOValues)
232 {
233     // Test where works
234     {
235         MockServices services;
236         std::vector<int> gpioValues{1, 1, 1};
237         EXPECT_CALL(services, getGPIOValues("abc_382%#, zy"))
238             .Times(1)
239             .WillOnce(Return(gpioValues));
240 
241         std::string name{"ABC_382%#, ZY"};
242         std::vector<std::unique_ptr<Rail>> rails;
243         uint8_t bus{3};
244         uint16_t address{0x72};
245         PMBusDriverDevice device{name, std::move(rails), services, bus,
246                                  address};
247 
248         EXPECT_TRUE(device.getGPIOValues(services) == gpioValues);
249     }
250 
251     // Test where fails with exception
252     {
253         MockServices services;
254         EXPECT_CALL(services, getGPIOValues("xyz_pseq"))
255             .Times(1)
256             .WillOnce(
257                 Throw(std::runtime_error{"libgpiod: Unable to open chip"}));
258 
259         std::string name{"XYZ_PSEQ"};
260         std::vector<std::unique_ptr<Rail>> rails;
261         uint8_t bus{3};
262         uint16_t address{0x72};
263         PMBusDriverDevice device{name, std::move(rails), services, bus,
264                                  address};
265 
266         try
267         {
268             device.getGPIOValues(services);
269             ADD_FAILURE() << "Should not have reached this line.";
270         }
271         catch (const std::exception& e)
272         {
273             EXPECT_STREQ(e.what(),
274                          "Unable to read GPIO values from device XYZ_PSEQ "
275                          "using label xyz_pseq: "
276                          "libgpiod: Unable to open chip");
277         }
278     }
279 }
280 
281 TEST_F(PMBusDriverDeviceTests, GetStatusWord)
282 {
283     // Test where works
284     {
285         MockServices services;
286 
287         std::string name{"xyz_pseq"};
288         std::vector<std::unique_ptr<Rail>> rails;
289         uint8_t bus{3};
290         uint16_t address{0x72};
291         PMBusDriverDevice device{name, std::move(rails), services, bus,
292                                  address};
293 
294         MockPMBus& pmbus = static_cast<MockPMBus&>(device.getPMBusInterface());
295         EXPECT_CALL(pmbus, read("status13", Type::Debug, true))
296             .Times(1)
297             .WillOnce(Return(0x1234));
298 
299         uint8_t page{13};
300         EXPECT_EQ(device.getStatusWord(page), 0x1234);
301     }
302 
303     // Test where fails with exception
304     {
305         MockServices services;
306 
307         std::string name{"xyz_pseq"};
308         std::vector<std::unique_ptr<Rail>> rails;
309         uint8_t bus{3};
310         uint16_t address{0x72};
311         PMBusDriverDevice device{name, std::move(rails), services, bus,
312                                  address};
313 
314         MockPMBus& pmbus = static_cast<MockPMBus&>(device.getPMBusInterface());
315         EXPECT_CALL(pmbus, read("status0", Type::Debug, true))
316             .Times(1)
317             .WillOnce(Throw(std::runtime_error{"File does not exist"}));
318 
319         try
320         {
321             uint8_t page{0};
322             device.getStatusWord(page);
323             ADD_FAILURE() << "Should not have reached this line.";
324         }
325         catch (const std::exception& e)
326         {
327             EXPECT_STREQ(
328                 e.what(),
329                 "Unable to read STATUS_WORD for PAGE 0 of device xyz_pseq: "
330                 "File does not exist");
331         }
332     }
333 }
334 
335 TEST_F(PMBusDriverDeviceTests, GetStatusVout)
336 {
337     // Test where works
338     {
339         MockServices services;
340 
341         std::string name{"xyz_pseq"};
342         std::vector<std::unique_ptr<Rail>> rails;
343         uint8_t bus{3};
344         uint16_t address{0x72};
345         PMBusDriverDevice device{name, std::move(rails), services, bus,
346                                  address};
347 
348         MockPMBus& pmbus = static_cast<MockPMBus&>(device.getPMBusInterface());
349         EXPECT_CALL(pmbus, read("status13_vout", Type::Debug, true))
350             .Times(1)
351             .WillOnce(Return(0xde));
352 
353         uint8_t page{13};
354         EXPECT_EQ(device.getStatusVout(page), 0xde);
355     }
356 
357     // Test where fails with exception
358     {
359         MockServices services;
360 
361         std::string name{"xyz_pseq"};
362         std::vector<std::unique_ptr<Rail>> rails;
363         uint8_t bus{3};
364         uint16_t address{0x72};
365         PMBusDriverDevice device{name, std::move(rails), services, bus,
366                                  address};
367 
368         MockPMBus& pmbus = static_cast<MockPMBus&>(device.getPMBusInterface());
369         EXPECT_CALL(pmbus, read("status0_vout", Type::Debug, true))
370             .Times(1)
371             .WillOnce(Throw(std::runtime_error{"File does not exist"}));
372 
373         try
374         {
375             uint8_t page{0};
376             device.getStatusVout(page);
377             ADD_FAILURE() << "Should not have reached this line.";
378         }
379         catch (const std::exception& e)
380         {
381             EXPECT_STREQ(
382                 e.what(),
383                 "Unable to read STATUS_VOUT for PAGE 0 of device xyz_pseq: "
384                 "File does not exist");
385         }
386     }
387 }
388 
389 TEST_F(PMBusDriverDeviceTests, GetReadVout)
390 {
391     // Test where works
392     {
393         // Create simulated hwmon voltage label file
394         createFile("in13_label"); // PAGE 9 -> file number 13
395 
396         MockServices services;
397 
398         std::string name{"xyz_pseq"};
399         std::vector<std::unique_ptr<Rail>> rails;
400         uint8_t bus{3};
401         uint16_t address{0x72};
402         PMBusDriverDevice device{name, std::move(rails), services, bus,
403                                  address};
404 
405         MockPMBus& pmbus = static_cast<MockPMBus&>(device.getPMBusInterface());
406         EXPECT_CALL(pmbus, getPath(Type::Hwmon))
407             .Times(1)
408             .WillOnce(Return(tempDirPath));
409         EXPECT_CALL(pmbus, readString("in13_label", Type::Hwmon))
410             .Times(1)
411             .WillOnce(Return("vout10")); // PAGE number 9 + 1
412         EXPECT_CALL(pmbus, readString("in13_input", Type::Hwmon))
413             .Times(1)
414             .WillOnce(Return("851"));
415 
416         uint8_t page{9};
417         EXPECT_EQ(device.getReadVout(page), 0.851);
418     }
419 
420     // Test where fails
421     {
422         // Create simulated hwmon voltage label file
423         createFile("in13_label"); // PAGE 8 -> file number 13
424 
425         MockServices services;
426 
427         std::string name{"xyz_pseq"};
428         std::vector<std::unique_ptr<Rail>> rails;
429         uint8_t bus{3};
430         uint16_t address{0x72};
431         PMBusDriverDevice device{name, std::move(rails), services, bus,
432                                  address};
433 
434         MockPMBus& pmbus = static_cast<MockPMBus&>(device.getPMBusInterface());
435         EXPECT_CALL(pmbus, getPath(Type::Hwmon))
436             .Times(1)
437             .WillOnce(Return(tempDirPath));
438         EXPECT_CALL(pmbus, readString("in13_label", Type::Hwmon))
439             .Times(1)
440             .WillOnce(Return("vout9")); // PAGE number 8 + 1
441 
442         try
443         {
444             uint8_t page{9};
445             device.getReadVout(page);
446             ADD_FAILURE() << "Should not have reached this line.";
447         }
448         catch (const std::exception& e)
449         {
450             EXPECT_STREQ(
451                 e.what(),
452                 "Unable to read READ_VOUT for PAGE 9 of device xyz_pseq: "
453                 "Unable to find hwmon file number for PAGE 9 of device xyz_pseq");
454         }
455     }
456 }
457 
458 TEST_F(PMBusDriverDeviceTests, GetVoutUVFaultLimit)
459 {
460     // Test where works
461     {
462         // Create simulated hwmon voltage label file
463         createFile("in1_label"); // PAGE 6 -> file number 1
464 
465         MockServices services;
466 
467         std::string name{"xyz_pseq"};
468         std::vector<std::unique_ptr<Rail>> rails;
469         uint8_t bus{3};
470         uint16_t address{0x72};
471         PMBusDriverDevice device{name, std::move(rails), services, bus,
472                                  address};
473 
474         MockPMBus& pmbus = static_cast<MockPMBus&>(device.getPMBusInterface());
475         EXPECT_CALL(pmbus, getPath(Type::Hwmon))
476             .Times(1)
477             .WillOnce(Return(tempDirPath));
478         EXPECT_CALL(pmbus, readString("in1_label", Type::Hwmon))
479             .Times(1)
480             .WillOnce(Return("vout7")); // PAGE number 6 + 1
481         EXPECT_CALL(pmbus, readString("in1_lcrit", Type::Hwmon))
482             .Times(1)
483             .WillOnce(Return("1329"));
484 
485         uint8_t page{6};
486         EXPECT_EQ(device.getVoutUVFaultLimit(page), 1.329);
487     }
488 
489     // Test where fails
490     {
491         // Create simulated hwmon voltage label file
492         createFile("in1_label"); // PAGE 7 -> file number 1
493 
494         MockServices services;
495 
496         std::string name{"xyz_pseq"};
497         std::vector<std::unique_ptr<Rail>> rails;
498         uint8_t bus{3};
499         uint16_t address{0x72};
500         PMBusDriverDevice device{name, std::move(rails), services, bus,
501                                  address};
502 
503         MockPMBus& pmbus = static_cast<MockPMBus&>(device.getPMBusInterface());
504         EXPECT_CALL(pmbus, getPath(Type::Hwmon))
505             .Times(1)
506             .WillOnce(Return(tempDirPath));
507         EXPECT_CALL(pmbus, readString("in1_label", Type::Hwmon))
508             .Times(1)
509             .WillOnce(Return("vout8")); // PAGE number 7 + 1
510 
511         try
512         {
513             uint8_t page{6};
514             device.getVoutUVFaultLimit(page);
515             ADD_FAILURE() << "Should not have reached this line.";
516         }
517         catch (const std::exception& e)
518         {
519             EXPECT_STREQ(
520                 e.what(),
521                 "Unable to read VOUT_UV_FAULT_LIMIT for PAGE 6 of device xyz_pseq: "
522                 "Unable to find hwmon file number for PAGE 6 of device xyz_pseq");
523         }
524     }
525 }
526 
527 TEST_F(PMBusDriverDeviceTests, GetPageToFileNumberMap)
528 {
529     // Test where works: No voltage label files/mappings found
530     {
531         // Create simulated hwmon files.  None are valid voltage label files.
532         createFile("in1_input");   // Not a label file
533         createFile("in9_lcrit");   // Not a label file
534         createFile("in_label");    // Invalid voltage label file name
535         createFile("in9a_label");  // Invalid voltage label file name
536         createFile("fan3_label");  // Not a voltage label file
537         createFile("temp8_label"); // Not a voltage label file
538 
539         MockServices services;
540 
541         std::string name{"xyz_pseq"};
542         std::vector<std::unique_ptr<Rail>> rails;
543         uint8_t bus{3};
544         uint16_t address{0x72};
545         PMBusDriverDevice device{name, std::move(rails), services, bus,
546                                  address};
547 
548         MockPMBus& pmbus = static_cast<MockPMBus&>(device.getPMBusInterface());
549         EXPECT_CALL(pmbus, getPath(Type::Hwmon))
550             .Times(1)
551             .WillOnce(Return(tempDirPath));
552         EXPECT_CALL(pmbus, readString).Times(0);
553 
554         const std::map<uint8_t, unsigned int>& map =
555             device.getPageToFileNumberMap();
556         EXPECT_TRUE(map.empty());
557     }
558 
559     // Test where works: Multiple voltage label files/mappings found
560     {
561         // Create simulated hwmon files
562         createFile("in9_label");   // PAGE 3 -> file number 9
563         createFile("in13_label");  // PAGE 7 -> file number 13
564         createFile("in0_label");   // PAGE 12 -> file number 0
565         createFile("in11_label");  // No mapping; invalid contents
566         createFile("in12_label");  // No mapping; invalid contents
567         createFile("in1_input");   // Not a label file
568         createFile("in7_lcrit");   // Not a label file
569         createFile("fan3_label");  // Not a voltage label file
570         createFile("temp8_label"); // Not a voltage label file
571 
572         MockServices services;
573 
574         std::string name{"xyz_pseq"};
575         std::vector<std::unique_ptr<Rail>> rails;
576         uint8_t bus{3};
577         uint16_t address{0x72};
578         PMBusDriverDevice device{name, std::move(rails), services, bus,
579                                  address};
580 
581         MockPMBus& pmbus = static_cast<MockPMBus&>(device.getPMBusInterface());
582         EXPECT_CALL(pmbus, getPath(Type::Hwmon))
583             .Times(1)
584             .WillOnce(Return(tempDirPath));
585         EXPECT_CALL(pmbus, readString("in9_label", Type::Hwmon))
586             .Times(1)
587             .WillOnce(Return("vout4")); // PAGE number 3 + 1
588         EXPECT_CALL(pmbus, readString("in13_label", Type::Hwmon))
589             .Times(1)
590             .WillOnce(Return("vout8")); // PAGE number 7 + 1
591         EXPECT_CALL(pmbus, readString("in0_label", Type::Hwmon))
592             .Times(1)
593             .WillOnce(Return("vout13")); // PAGE number 12 + 1
594         EXPECT_CALL(pmbus, readString("in11_label", Type::Hwmon))
595             .Times(1)
596             .WillOnce(Return("vout")); // Invalid format
597         EXPECT_CALL(pmbus, readString("in12_label", Type::Hwmon))
598             .Times(1)
599             .WillOnce(Return("vout13a")); // Invalid format
600 
601         const std::map<uint8_t, unsigned int>& map =
602             device.getPageToFileNumberMap();
603         EXPECT_EQ(map.size(), 3);
604         EXPECT_EQ(map.at(uint8_t{3}), 9);
605         EXPECT_EQ(map.at(uint8_t{7}), 13);
606         EXPECT_EQ(map.at(uint8_t{12}), 0);
607     }
608 
609     // Test where fails: hwmon directory path is actually a file
610     {
611         // Create file that will be returned as the hwmon directory path
612         createFile("in9_label");
613 
614         MockServices services;
615 
616         std::string name{"xyz_pseq"};
617         std::vector<std::unique_ptr<Rail>> rails;
618         uint8_t bus{3};
619         uint16_t address{0x72};
620         PMBusDriverDevice device{name, std::move(rails), services, bus,
621                                  address};
622 
623         MockPMBus& pmbus = static_cast<MockPMBus&>(device.getPMBusInterface());
624         EXPECT_CALL(pmbus, getPath(Type::Hwmon))
625             .Times(1)
626             .WillOnce(Return(tempDirPath / "in9_label"));
627         EXPECT_CALL(pmbus, readString).Times(0);
628 
629         const std::map<uint8_t, unsigned int>& map =
630             device.getPageToFileNumberMap();
631         EXPECT_TRUE(map.empty());
632     }
633 
634     // Test where fails: hwmon directory path does not exist
635     {
636         MockServices services;
637 
638         std::string name{"xyz_pseq"};
639         std::vector<std::unique_ptr<Rail>> rails;
640         uint8_t bus{3};
641         uint16_t address{0x72};
642         PMBusDriverDevice device{name, std::move(rails), services, bus,
643                                  address};
644 
645         MockPMBus& pmbus = static_cast<MockPMBus&>(device.getPMBusInterface());
646         EXPECT_CALL(pmbus, getPath(Type::Hwmon))
647             .Times(1)
648             .WillOnce(Return(tempDirPath / "does_not_exist"));
649         EXPECT_CALL(pmbus, readString).Times(0);
650 
651         const std::map<uint8_t, unsigned int>& map =
652             device.getPageToFileNumberMap();
653         EXPECT_TRUE(map.empty());
654     }
655 
656     // Test where fails: hwmon directory path is not readable
657     {
658         // Create simulated hwmon files
659         createFile("in9_label");
660         createFile("in13_label");
661         createFile("in0_label");
662 
663         // Change temporary directory to be unreadable
664         fs::permissions(tempDirPath, fs::perms::none);
665 
666         MockServices services;
667 
668         std::string name{"xyz_pseq"};
669         std::vector<std::unique_ptr<Rail>> rails;
670         uint8_t bus{3};
671         uint16_t address{0x72};
672         PMBusDriverDevice device{name, std::move(rails), services, bus,
673                                  address};
674 
675         MockPMBus& pmbus = static_cast<MockPMBus&>(device.getPMBusInterface());
676         EXPECT_CALL(pmbus, getPath(Type::Hwmon))
677             .Times(1)
678             .WillOnce(Return(tempDirPath));
679         EXPECT_CALL(pmbus, readString).Times(0);
680 
681         try
682         {
683             device.getPageToFileNumberMap();
684             ADD_FAILURE() << "Should not have reached this line.";
685         }
686         catch (const std::exception& e)
687         {
688             // Error message varies
689         }
690 
691         // Change temporary directory to be readable/writable
692         fs::permissions(tempDirPath, fs::perms::owner_all);
693     }
694 }
695 
696 TEST_F(PMBusDriverDeviceTests, GetFileNumber)
697 {
698     // Test where works
699     {
700         // Create simulated hwmon voltage label files
701         createFile("in0_label");  // PAGE 6 -> file number 0
702         createFile("in13_label"); // PAGE 9 -> file number 13
703 
704         MockServices services;
705 
706         std::string name{"xyz_pseq"};
707         std::vector<std::unique_ptr<Rail>> rails;
708         uint8_t bus{3};
709         uint16_t address{0x72};
710         PMBusDriverDevice device{name, std::move(rails), services, bus,
711                                  address};
712 
713         MockPMBus& pmbus = static_cast<MockPMBus&>(device.getPMBusInterface());
714         EXPECT_CALL(pmbus, getPath(Type::Hwmon))
715             .Times(1)
716             .WillOnce(Return(tempDirPath));
717         EXPECT_CALL(pmbus, readString("in0_label", Type::Hwmon))
718             .Times(1)
719             .WillOnce(Return("vout7")); // PAGE number 6 + 1
720         EXPECT_CALL(pmbus, readString("in13_label", Type::Hwmon))
721             .Times(1)
722             .WillOnce(Return("vout10")); // PAGE number 9 + 1
723 
724         // Map was empty and needs to be built
725         uint8_t page{6};
726         EXPECT_EQ(device.getFileNumber(page), 0);
727 
728         // Map had already been built
729         page = 9;
730         EXPECT_EQ(device.getFileNumber(page), 13);
731     }
732 
733     // Test where fails: No mapping for specified PMBus PAGE
734     {
735         // Create simulated hwmon voltage label files
736         createFile("in0_label");  // PAGE 6 -> file number 0
737         createFile("in13_label"); // PAGE 9 -> file number 13
738 
739         MockServices services;
740 
741         std::string name{"xyz_pseq"};
742         std::vector<std::unique_ptr<Rail>> rails;
743         uint8_t bus{3};
744         uint16_t address{0x72};
745         PMBusDriverDevice device{name, std::move(rails), services, bus,
746                                  address};
747 
748         MockPMBus& pmbus = static_cast<MockPMBus&>(device.getPMBusInterface());
749         EXPECT_CALL(pmbus, getPath(Type::Hwmon))
750             .Times(1)
751             .WillOnce(Return(tempDirPath));
752         EXPECT_CALL(pmbus, readString("in0_label", Type::Hwmon))
753             .Times(1)
754             .WillOnce(Return("vout7")); // PAGE number 6 + 1
755         EXPECT_CALL(pmbus, readString("in13_label", Type::Hwmon))
756             .Times(1)
757             .WillOnce(Return("vout10")); // PAGE number 9 + 1
758 
759         try
760         {
761             uint8_t page{13};
762             device.getFileNumber(page);
763             ADD_FAILURE() << "Should not have reached this line.";
764         }
765         catch (const std::exception& e)
766         {
767             EXPECT_STREQ(
768                 e.what(),
769                 "Unable to find hwmon file number for PAGE 13 of device xyz_pseq");
770         }
771     }
772 }
773 
774 TEST_F(PMBusDriverDeviceTests, PrepareForPgoodFaultDetection)
775 {
776     // This is a protected method and cannot be called directly from a gtest.
777     // Call findPgoodFault() which calls prepareForPgoodFaultDetection().
778 
779     // Create simulated hwmon voltage label file
780     createFile("in1_label"); // PAGE 6 -> file number 1
781 
782     MockServices services;
783     std::vector<int> gpioValues{1, 1, 1};
784     EXPECT_CALL(services, getGPIOValues("xyz_pseq"))
785         .Times(1)
786         .WillOnce(Return(gpioValues));
787 
788     std::string name{"xyz_pseq"};
789     std::vector<std::unique_ptr<Rail>> rails;
790     uint8_t bus{3};
791     uint16_t address{0x72};
792     PMBusDriverDevice device{name, std::move(rails), services, bus, address};
793 
794     // Methods that get hwmon file info should be called twice
795     MockPMBus& pmbus = static_cast<MockPMBus&>(device.getPMBusInterface());
796     EXPECT_CALL(pmbus, getPath(Type::Hwmon))
797         .Times(2)
798         .WillRepeatedly(Return(tempDirPath));
799     EXPECT_CALL(pmbus, readString("in1_label", Type::Hwmon))
800         .Times(2)
801         .WillRepeatedly(Return("vout7")); // PAGE number 6 + 1
802 
803     // Map was empty and needs to be built
804     uint8_t page{6};
805     EXPECT_EQ(device.getFileNumber(page), 1);
806 
807     // Call findPgoodFault() which calls prepareForPgoodFaultDetection() which
808     // rebuilds the map.
809     std::string powerSupplyError{};
810     std::map<std::string, std::string> additionalData{};
811     std::string error = device.findPgoodFault(services, powerSupplyError,
812                                               additionalData);
813     EXPECT_TRUE(error.empty());
814     EXPECT_EQ(additionalData.size(), 0);
815 
816     EXPECT_EQ(device.getFileNumber(page), 1);
817 }
818