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 */
PMBusDriverDeviceTests()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 */
createRail(const std::string & name,uint8_t pageNum)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 */
createFile(const std::string & name,const std::string & contents="")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
TEST_F(PMBusDriverDeviceTests,Constructor)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
TEST_F(PMBusDriverDeviceTests,GetBus)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
TEST_F(PMBusDriverDeviceTests,GetAddress)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
TEST_F(PMBusDriverDeviceTests,GetDriverName)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
TEST_F(PMBusDriverDeviceTests,GetInstance)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
TEST_F(PMBusDriverDeviceTests,GetPMBusInterface)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
TEST_F(PMBusDriverDeviceTests,GetGPIOValues)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
TEST_F(PMBusDriverDeviceTests,GetStatusWord)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
TEST_F(PMBusDriverDeviceTests,GetStatusVout)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
TEST_F(PMBusDriverDeviceTests,GetReadVout)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
TEST_F(PMBusDriverDeviceTests,GetVoutUVFaultLimit)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
TEST_F(PMBusDriverDeviceTests,GetPageToFileNumberMap)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
TEST_F(PMBusDriverDeviceTests,GetFileNumber)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
TEST_F(PMBusDriverDeviceTests,PrepareForPgoodFaultDetection)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 =
812 device.findPgoodFault(services, powerSupplyError, additionalData);
813 EXPECT_TRUE(error.empty());
814 EXPECT_EQ(additionalData.size(), 0);
815
816 EXPECT_EQ(device.getFileNumber(page), 1);
817 }
818