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<PgoodGPIO> 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 uint8_t bus{3};
116 uint16_t address{0x72};
117 std::string powerControlGPIOName{"power-chassis-control"};
118 std::string powerGoodGPIOName{"power-chassis-good"};
119 std::vector<std::unique_ptr<Rail>> rails;
120 rails.emplace_back(createRail("VDD", 5));
121 rails.emplace_back(createRail("VIO", 7));
122 PMBusDriverDevice device{
123 name,
124 bus,
125 address,
126 powerControlGPIOName,
127 powerGoodGPIOName,
128 std::move(rails),
129 services};
130
131 EXPECT_EQ(device.getName(), name);
132 EXPECT_EQ(device.getBus(), bus);
133 EXPECT_EQ(device.getAddress(), address);
134 EXPECT_EQ(device.getPowerControlGPIOName(), powerControlGPIOName);
135 EXPECT_EQ(device.getPowerGoodGPIOName(), powerGoodGPIOName);
136 EXPECT_EQ(device.getRails().size(), 2);
137 EXPECT_EQ(device.getRails()[0]->getName(), "VDD");
138 EXPECT_EQ(device.getRails()[1]->getName(), "VIO");
139 EXPECT_EQ(device.getDriverName(), "");
140 EXPECT_EQ(device.getInstance(), 0);
141 EXPECT_NE(&(device.getPMBusInterface()), nullptr);
142 }
143
144 // Test where works; optional parameters specified
145 {
146 MockServices services;
147
148 std::string name{"XYZ_PSEQ"};
149 uint8_t bus{3};
150 uint16_t address{0x72};
151 std::string powerControlGPIOName{"power-on"};
152 std::string powerGoodGPIOName{"pgood"};
153 std::vector<std::unique_ptr<Rail>> rails;
154 rails.emplace_back(createRail("VDD", 5));
155 rails.emplace_back(createRail("VIO", 7));
156 std::string driverName{"xyzdev"};
157 size_t instance{3};
158 PMBusDriverDevice device{
159 name,
160 bus,
161 address,
162 powerControlGPIOName,
163 powerGoodGPIOName,
164 std::move(rails),
165 services,
166 driverName,
167 instance};
168
169 EXPECT_EQ(device.getName(), name);
170 EXPECT_EQ(device.getBus(), bus);
171 EXPECT_EQ(device.getAddress(), address);
172 EXPECT_EQ(device.getPowerControlGPIOName(), powerControlGPIOName);
173 EXPECT_EQ(device.getPowerGoodGPIOName(), powerGoodGPIOName);
174 EXPECT_EQ(device.getRails().size(), 2);
175 EXPECT_EQ(device.getRails()[0]->getName(), "VDD");
176 EXPECT_EQ(device.getRails()[1]->getName(), "VIO");
177 EXPECT_EQ(device.getDriverName(), driverName);
178 EXPECT_EQ(device.getInstance(), instance);
179 EXPECT_NE(&(device.getPMBusInterface()), nullptr);
180 }
181 }
182
TEST_F(PMBusDriverDeviceTests,GetDriverName)183 TEST_F(PMBusDriverDeviceTests, GetDriverName)
184 {
185 MockServices services;
186
187 std::string name{"XYZ_PSEQ"};
188 uint8_t bus{3};
189 uint16_t address{0x72};
190 std::string powerControlGPIOName{"power-chassis-control"};
191 std::string powerGoodGPIOName{"power-chassis-good"};
192 std::vector<std::unique_ptr<Rail>> rails;
193 std::string driverName{"xyzdev"};
194 PMBusDriverDevice device{
195 name,
196 bus,
197 address,
198 powerControlGPIOName,
199 powerGoodGPIOName,
200 std::move(rails),
201 services,
202 driverName};
203
204 EXPECT_EQ(device.getDriverName(), driverName);
205 }
206
TEST_F(PMBusDriverDeviceTests,GetInstance)207 TEST_F(PMBusDriverDeviceTests, GetInstance)
208 {
209 MockServices services;
210
211 std::string name{"XYZ_PSEQ"};
212 uint8_t bus{3};
213 uint16_t address{0x72};
214 std::string powerControlGPIOName{"power-chassis-control"};
215 std::string powerGoodGPIOName{"power-chassis-good"};
216 std::vector<std::unique_ptr<Rail>> rails;
217 std::string driverName{"xyzdev"};
218 size_t instance{3};
219 PMBusDriverDevice device{
220 name,
221 bus,
222 address,
223 powerControlGPIOName,
224 powerGoodGPIOName,
225 std::move(rails),
226 services,
227 driverName,
228 instance};
229
230 EXPECT_EQ(device.getInstance(), instance);
231 }
232
TEST_F(PMBusDriverDeviceTests,GetPMBusInterface)233 TEST_F(PMBusDriverDeviceTests, GetPMBusInterface)
234 {
235 MockServices services;
236
237 std::string name{"XYZ_PSEQ"};
238 uint8_t bus{3};
239 uint16_t address{0x72};
240 std::string powerControlGPIOName{"power-chassis-control"};
241 std::string powerGoodGPIOName{"power-chassis-good"};
242 std::vector<std::unique_ptr<Rail>> rails;
243 PMBusDriverDevice device{
244 name,
245 bus,
246 address,
247 powerControlGPIOName,
248 powerGoodGPIOName,
249 std::move(rails),
250 services};
251
252 EXPECT_NE(&(device.getPMBusInterface()), nullptr);
253 }
254
TEST_F(PMBusDriverDeviceTests,GetGPIOValues)255 TEST_F(PMBusDriverDeviceTests, GetGPIOValues)
256 {
257 // Test where works
258 {
259 MockServices services;
260 std::vector<int> gpioValues{1, 1, 1};
261 EXPECT_CALL(services, getGPIOValues("abc_382%#, zy"))
262 .Times(1)
263 .WillOnce(Return(gpioValues));
264
265 std::string name{"ABC_382%#, ZY"};
266 uint8_t bus{3};
267 uint16_t address{0x72};
268 std::string powerControlGPIOName{"power-chassis-control"};
269 std::string powerGoodGPIOName{"power-chassis-good"};
270 std::vector<std::unique_ptr<Rail>> rails;
271 PMBusDriverDevice device{
272 name,
273 bus,
274 address,
275 powerControlGPIOName,
276 powerGoodGPIOName,
277 std::move(rails),
278 services};
279
280 EXPECT_TRUE(device.getGPIOValues(services) == gpioValues);
281 }
282
283 // Test where fails with exception
284 {
285 MockServices services;
286 EXPECT_CALL(services, getGPIOValues("xyz_pseq"))
287 .Times(1)
288 .WillOnce(
289 Throw(std::runtime_error{"libgpiod: Unable to open chip"}));
290
291 std::string name{"XYZ_PSEQ"};
292 uint8_t bus{3};
293 uint16_t address{0x72};
294 std::string powerControlGPIOName{"power-chassis-control"};
295 std::string powerGoodGPIOName{"power-chassis-good"};
296 std::vector<std::unique_ptr<Rail>> rails;
297 PMBusDriverDevice device{
298 name,
299 bus,
300 address,
301 powerControlGPIOName,
302 powerGoodGPIOName,
303 std::move(rails),
304 services};
305
306 try
307 {
308 device.getGPIOValues(services);
309 ADD_FAILURE() << "Should not have reached this line.";
310 }
311 catch (const std::exception& e)
312 {
313 EXPECT_STREQ(e.what(),
314 "Unable to read GPIO values from device XYZ_PSEQ "
315 "using label xyz_pseq: "
316 "libgpiod: Unable to open chip");
317 }
318 }
319 }
320
TEST_F(PMBusDriverDeviceTests,GetStatusWord)321 TEST_F(PMBusDriverDeviceTests, GetStatusWord)
322 {
323 // Test where works
324 {
325 MockServices services;
326
327 std::string name{"xyz_pseq"};
328 uint8_t bus{3};
329 uint16_t address{0x72};
330 std::string powerControlGPIOName{"power-chassis-control"};
331 std::string powerGoodGPIOName{"power-chassis-good"};
332 std::vector<std::unique_ptr<Rail>> rails;
333 PMBusDriverDevice device{
334 name,
335 bus,
336 address,
337 powerControlGPIOName,
338 powerGoodGPIOName,
339 std::move(rails),
340 services};
341
342 MockPMBus& pmbus = static_cast<MockPMBus&>(device.getPMBusInterface());
343 EXPECT_CALL(pmbus, read("status13", Type::Debug, true))
344 .Times(1)
345 .WillOnce(Return(0x1234));
346
347 uint8_t page{13};
348 EXPECT_EQ(device.getStatusWord(page), 0x1234);
349 }
350
351 // Test where fails with exception
352 {
353 MockServices services;
354
355 std::string name{"xyz_pseq"};
356 uint8_t bus{3};
357 uint16_t address{0x72};
358 std::string powerControlGPIOName{"power-chassis-control"};
359 std::string powerGoodGPIOName{"power-chassis-good"};
360 std::vector<std::unique_ptr<Rail>> rails;
361 PMBusDriverDevice device{
362 name,
363 bus,
364 address,
365 powerControlGPIOName,
366 powerGoodGPIOName,
367 std::move(rails),
368 services};
369
370 MockPMBus& pmbus = static_cast<MockPMBus&>(device.getPMBusInterface());
371 EXPECT_CALL(pmbus, read("status0", Type::Debug, true))
372 .Times(1)
373 .WillOnce(Throw(std::runtime_error{"File does not exist"}));
374
375 try
376 {
377 uint8_t page{0};
378 device.getStatusWord(page);
379 ADD_FAILURE() << "Should not have reached this line.";
380 }
381 catch (const std::exception& e)
382 {
383 EXPECT_STREQ(
384 e.what(),
385 "Unable to read STATUS_WORD for PAGE 0 of device xyz_pseq: "
386 "File does not exist");
387 }
388 }
389 }
390
TEST_F(PMBusDriverDeviceTests,GetStatusVout)391 TEST_F(PMBusDriverDeviceTests, GetStatusVout)
392 {
393 // Test where works
394 {
395 MockServices services;
396
397 std::string name{"xyz_pseq"};
398 uint8_t bus{3};
399 uint16_t address{0x72};
400 std::string powerControlGPIOName{"power-chassis-control"};
401 std::string powerGoodGPIOName{"power-chassis-good"};
402 std::vector<std::unique_ptr<Rail>> rails;
403 PMBusDriverDevice device{
404 name,
405 bus,
406 address,
407 powerControlGPIOName,
408 powerGoodGPIOName,
409 std::move(rails),
410 services};
411
412 MockPMBus& pmbus = static_cast<MockPMBus&>(device.getPMBusInterface());
413 EXPECT_CALL(pmbus, read("status13_vout", Type::Debug, true))
414 .Times(1)
415 .WillOnce(Return(0xde));
416
417 uint8_t page{13};
418 EXPECT_EQ(device.getStatusVout(page), 0xde);
419 }
420
421 // Test where fails with exception
422 {
423 MockServices services;
424
425 std::string name{"xyz_pseq"};
426 uint8_t bus{3};
427 uint16_t address{0x72};
428 std::string powerControlGPIOName{"power-chassis-control"};
429 std::string powerGoodGPIOName{"power-chassis-good"};
430 std::vector<std::unique_ptr<Rail>> rails;
431 PMBusDriverDevice device{
432 name,
433 bus,
434 address,
435 powerControlGPIOName,
436 powerGoodGPIOName,
437 std::move(rails),
438 services};
439
440 MockPMBus& pmbus = static_cast<MockPMBus&>(device.getPMBusInterface());
441 EXPECT_CALL(pmbus, read("status0_vout", Type::Debug, true))
442 .Times(1)
443 .WillOnce(Throw(std::runtime_error{"File does not exist"}));
444
445 try
446 {
447 uint8_t page{0};
448 device.getStatusVout(page);
449 ADD_FAILURE() << "Should not have reached this line.";
450 }
451 catch (const std::exception& e)
452 {
453 EXPECT_STREQ(
454 e.what(),
455 "Unable to read STATUS_VOUT for PAGE 0 of device xyz_pseq: "
456 "File does not exist");
457 }
458 }
459 }
460
TEST_F(PMBusDriverDeviceTests,GetReadVout)461 TEST_F(PMBusDriverDeviceTests, GetReadVout)
462 {
463 // Test where works
464 {
465 // Create simulated hwmon voltage label file
466 createFile("in13_label"); // PAGE 9 -> file number 13
467
468 MockServices services;
469
470 std::string name{"xyz_pseq"};
471 uint8_t bus{3};
472 uint16_t address{0x72};
473 std::string powerControlGPIOName{"power-chassis-control"};
474 std::string powerGoodGPIOName{"power-chassis-good"};
475 std::vector<std::unique_ptr<Rail>> rails;
476 PMBusDriverDevice device{
477 name,
478 bus,
479 address,
480 powerControlGPIOName,
481 powerGoodGPIOName,
482 std::move(rails),
483 services};
484
485 MockPMBus& pmbus = static_cast<MockPMBus&>(device.getPMBusInterface());
486 EXPECT_CALL(pmbus, getPath(Type::Hwmon))
487 .Times(1)
488 .WillOnce(Return(tempDirPath));
489 EXPECT_CALL(pmbus, readString("in13_label", Type::Hwmon))
490 .Times(1)
491 .WillOnce(Return("vout10")); // PAGE number 9 + 1
492 EXPECT_CALL(pmbus, readString("in13_input", Type::Hwmon))
493 .Times(1)
494 .WillOnce(Return("851"));
495
496 uint8_t page{9};
497 EXPECT_EQ(device.getReadVout(page), 0.851);
498 }
499
500 // Test where fails
501 {
502 // Create simulated hwmon voltage label file
503 createFile("in13_label"); // PAGE 8 -> file number 13
504
505 MockServices services;
506
507 std::string name{"xyz_pseq"};
508 uint8_t bus{3};
509 uint16_t address{0x72};
510 std::string powerControlGPIOName{"power-chassis-control"};
511 std::string powerGoodGPIOName{"power-chassis-good"};
512 std::vector<std::unique_ptr<Rail>> rails;
513 PMBusDriverDevice device{
514 name,
515 bus,
516 address,
517 powerControlGPIOName,
518 powerGoodGPIOName,
519 std::move(rails),
520 services};
521
522 MockPMBus& pmbus = static_cast<MockPMBus&>(device.getPMBusInterface());
523 EXPECT_CALL(pmbus, getPath(Type::Hwmon))
524 .Times(1)
525 .WillOnce(Return(tempDirPath));
526 EXPECT_CALL(pmbus, readString("in13_label", Type::Hwmon))
527 .Times(1)
528 .WillOnce(Return("vout9")); // PAGE number 8 + 1
529
530 try
531 {
532 uint8_t page{9};
533 device.getReadVout(page);
534 ADD_FAILURE() << "Should not have reached this line.";
535 }
536 catch (const std::exception& e)
537 {
538 EXPECT_STREQ(
539 e.what(),
540 "Unable to read READ_VOUT for PAGE 9 of device xyz_pseq: "
541 "Unable to find hwmon file number for PAGE 9 of device xyz_pseq");
542 }
543 }
544 }
545
TEST_F(PMBusDriverDeviceTests,GetVoutUVFaultLimit)546 TEST_F(PMBusDriverDeviceTests, GetVoutUVFaultLimit)
547 {
548 // Test where works
549 {
550 // Create simulated hwmon voltage label file
551 createFile("in1_label"); // PAGE 6 -> file number 1
552
553 MockServices services;
554
555 std::string name{"xyz_pseq"};
556 uint8_t bus{3};
557 uint16_t address{0x72};
558 std::string powerControlGPIOName{"power-chassis-control"};
559 std::string powerGoodGPIOName{"power-chassis-good"};
560 std::vector<std::unique_ptr<Rail>> rails;
561 PMBusDriverDevice device{
562 name,
563 bus,
564 address,
565 powerControlGPIOName,
566 powerGoodGPIOName,
567 std::move(rails),
568 services};
569
570 MockPMBus& pmbus = static_cast<MockPMBus&>(device.getPMBusInterface());
571 EXPECT_CALL(pmbus, getPath(Type::Hwmon))
572 .Times(1)
573 .WillOnce(Return(tempDirPath));
574 EXPECT_CALL(pmbus, readString("in1_label", Type::Hwmon))
575 .Times(1)
576 .WillOnce(Return("vout7")); // PAGE number 6 + 1
577 EXPECT_CALL(pmbus, readString("in1_lcrit", Type::Hwmon))
578 .Times(1)
579 .WillOnce(Return("1329"));
580
581 uint8_t page{6};
582 EXPECT_EQ(device.getVoutUVFaultLimit(page), 1.329);
583 }
584
585 // Test where fails
586 {
587 // Create simulated hwmon voltage label file
588 createFile("in1_label"); // PAGE 7 -> file number 1
589
590 MockServices services;
591
592 std::string name{"xyz_pseq"};
593 uint8_t bus{3};
594 uint16_t address{0x72};
595 std::string powerControlGPIOName{"power-chassis-control"};
596 std::string powerGoodGPIOName{"power-chassis-good"};
597 std::vector<std::unique_ptr<Rail>> rails;
598 PMBusDriverDevice device{
599 name,
600 bus,
601 address,
602 powerControlGPIOName,
603 powerGoodGPIOName,
604 std::move(rails),
605 services};
606
607 MockPMBus& pmbus = static_cast<MockPMBus&>(device.getPMBusInterface());
608 EXPECT_CALL(pmbus, getPath(Type::Hwmon))
609 .Times(1)
610 .WillOnce(Return(tempDirPath));
611 EXPECT_CALL(pmbus, readString("in1_label", Type::Hwmon))
612 .Times(1)
613 .WillOnce(Return("vout8")); // PAGE number 7 + 1
614
615 try
616 {
617 uint8_t page{6};
618 device.getVoutUVFaultLimit(page);
619 ADD_FAILURE() << "Should not have reached this line.";
620 }
621 catch (const std::exception& e)
622 {
623 EXPECT_STREQ(
624 e.what(),
625 "Unable to read VOUT_UV_FAULT_LIMIT for PAGE 6 of device xyz_pseq: "
626 "Unable to find hwmon file number for PAGE 6 of device xyz_pseq");
627 }
628 }
629 }
630
TEST_F(PMBusDriverDeviceTests,GetPageToFileNumberMap)631 TEST_F(PMBusDriverDeviceTests, GetPageToFileNumberMap)
632 {
633 // Test where works: No voltage label files/mappings found
634 {
635 // Create simulated hwmon files. None are valid voltage label files.
636 createFile("in1_input"); // Not a label file
637 createFile("in9_lcrit"); // Not a label file
638 createFile("in_label"); // Invalid voltage label file name
639 createFile("in9a_label"); // Invalid voltage label file name
640 createFile("fan3_label"); // Not a voltage label file
641 createFile("temp8_label"); // Not a voltage label file
642
643 MockServices services;
644
645 std::string name{"xyz_pseq"};
646 uint8_t bus{3};
647 uint16_t address{0x72};
648 std::string powerControlGPIOName{"power-chassis-control"};
649 std::string powerGoodGPIOName{"power-chassis-good"};
650 std::vector<std::unique_ptr<Rail>> rails;
651 PMBusDriverDevice device{
652 name,
653 bus,
654 address,
655 powerControlGPIOName,
656 powerGoodGPIOName,
657 std::move(rails),
658 services};
659
660 MockPMBus& pmbus = static_cast<MockPMBus&>(device.getPMBusInterface());
661 EXPECT_CALL(pmbus, getPath(Type::Hwmon))
662 .Times(1)
663 .WillOnce(Return(tempDirPath));
664 EXPECT_CALL(pmbus, readString).Times(0);
665
666 const std::map<uint8_t, unsigned int>& map =
667 device.getPageToFileNumberMap();
668 EXPECT_TRUE(map.empty());
669 }
670
671 // Test where works: Multiple voltage label files/mappings found
672 {
673 // Create simulated hwmon files
674 createFile("in9_label"); // PAGE 3 -> file number 9
675 createFile("in13_label"); // PAGE 7 -> file number 13
676 createFile("in0_label"); // PAGE 12 -> file number 0
677 createFile("in11_label"); // No mapping; invalid contents
678 createFile("in12_label"); // No mapping; invalid contents
679 createFile("in1_input"); // Not a label file
680 createFile("in7_lcrit"); // Not a label file
681 createFile("fan3_label"); // Not a voltage label file
682 createFile("temp8_label"); // Not a voltage label file
683
684 MockServices services;
685
686 std::string name{"xyz_pseq"};
687 uint8_t bus{3};
688 uint16_t address{0x72};
689 std::string powerControlGPIOName{"power-chassis-control"};
690 std::string powerGoodGPIOName{"power-chassis-good"};
691 std::vector<std::unique_ptr<Rail>> rails;
692 PMBusDriverDevice device{
693 name,
694 bus,
695 address,
696 powerControlGPIOName,
697 powerGoodGPIOName,
698 std::move(rails),
699 services};
700
701 MockPMBus& pmbus = static_cast<MockPMBus&>(device.getPMBusInterface());
702 EXPECT_CALL(pmbus, getPath(Type::Hwmon))
703 .Times(1)
704 .WillOnce(Return(tempDirPath));
705 EXPECT_CALL(pmbus, readString("in9_label", Type::Hwmon))
706 .Times(1)
707 .WillOnce(Return("vout4")); // PAGE number 3 + 1
708 EXPECT_CALL(pmbus, readString("in13_label", Type::Hwmon))
709 .Times(1)
710 .WillOnce(Return("vout8")); // PAGE number 7 + 1
711 EXPECT_CALL(pmbus, readString("in0_label", Type::Hwmon))
712 .Times(1)
713 .WillOnce(Return("vout13")); // PAGE number 12 + 1
714 EXPECT_CALL(pmbus, readString("in11_label", Type::Hwmon))
715 .Times(1)
716 .WillOnce(Return("vout")); // Invalid format
717 EXPECT_CALL(pmbus, readString("in12_label", Type::Hwmon))
718 .Times(1)
719 .WillOnce(Return("vout13a")); // Invalid format
720
721 const std::map<uint8_t, unsigned int>& map =
722 device.getPageToFileNumberMap();
723 EXPECT_EQ(map.size(), 3);
724 EXPECT_EQ(map.at(uint8_t{3}), 9);
725 EXPECT_EQ(map.at(uint8_t{7}), 13);
726 EXPECT_EQ(map.at(uint8_t{12}), 0);
727 }
728
729 // Test where fails: hwmon directory path is actually a file
730 {
731 // Create file that will be returned as the hwmon directory path
732 createFile("in9_label");
733
734 MockServices services;
735
736 std::string name{"xyz_pseq"};
737 uint8_t bus{3};
738 uint16_t address{0x72};
739 std::string powerControlGPIOName{"power-chassis-control"};
740 std::string powerGoodGPIOName{"power-chassis-good"};
741 std::vector<std::unique_ptr<Rail>> rails;
742 PMBusDriverDevice device{
743 name,
744 bus,
745 address,
746 powerControlGPIOName,
747 powerGoodGPIOName,
748 std::move(rails),
749 services};
750
751 MockPMBus& pmbus = static_cast<MockPMBus&>(device.getPMBusInterface());
752 EXPECT_CALL(pmbus, getPath(Type::Hwmon))
753 .Times(1)
754 .WillOnce(Return(tempDirPath / "in9_label"));
755 EXPECT_CALL(pmbus, readString).Times(0);
756
757 const std::map<uint8_t, unsigned int>& map =
758 device.getPageToFileNumberMap();
759 EXPECT_TRUE(map.empty());
760 }
761
762 // Test where fails: hwmon directory path does not exist
763 {
764 MockServices services;
765
766 std::string name{"xyz_pseq"};
767 uint8_t bus{3};
768 uint16_t address{0x72};
769 std::string powerControlGPIOName{"power-chassis-control"};
770 std::string powerGoodGPIOName{"power-chassis-good"};
771 std::vector<std::unique_ptr<Rail>> rails;
772 PMBusDriverDevice device{
773 name,
774 bus,
775 address,
776 powerControlGPIOName,
777 powerGoodGPIOName,
778 std::move(rails),
779 services};
780
781 MockPMBus& pmbus = static_cast<MockPMBus&>(device.getPMBusInterface());
782 EXPECT_CALL(pmbus, getPath(Type::Hwmon))
783 .Times(1)
784 .WillOnce(Return(tempDirPath / "does_not_exist"));
785 EXPECT_CALL(pmbus, readString).Times(0);
786
787 const std::map<uint8_t, unsigned int>& map =
788 device.getPageToFileNumberMap();
789 EXPECT_TRUE(map.empty());
790 }
791
792 // Test where fails: hwmon directory path is not readable
793 {
794 // Create simulated hwmon files
795 createFile("in9_label");
796 createFile("in13_label");
797 createFile("in0_label");
798
799 // Change temporary directory to be unreadable
800 fs::permissions(tempDirPath, fs::perms::none);
801
802 MockServices services;
803
804 std::string name{"xyz_pseq"};
805 uint8_t bus{3};
806 uint16_t address{0x72};
807 std::string powerControlGPIOName{"power-chassis-control"};
808 std::string powerGoodGPIOName{"power-chassis-good"};
809 std::vector<std::unique_ptr<Rail>> rails;
810 PMBusDriverDevice device{
811 name,
812 bus,
813 address,
814 powerControlGPIOName,
815 powerGoodGPIOName,
816 std::move(rails),
817 services};
818
819 MockPMBus& pmbus = static_cast<MockPMBus&>(device.getPMBusInterface());
820 EXPECT_CALL(pmbus, getPath(Type::Hwmon))
821 .Times(1)
822 .WillOnce(Return(tempDirPath));
823 EXPECT_CALL(pmbus, readString).Times(0);
824
825 try
826 {
827 device.getPageToFileNumberMap();
828 ADD_FAILURE() << "Should not have reached this line.";
829 }
830 catch (const std::exception& e)
831 {
832 // Error message varies
833 }
834
835 // Change temporary directory to be readable/writable
836 fs::permissions(tempDirPath, fs::perms::owner_all);
837 }
838 }
839
TEST_F(PMBusDriverDeviceTests,GetFileNumber)840 TEST_F(PMBusDriverDeviceTests, GetFileNumber)
841 {
842 // Test where works
843 {
844 // Create simulated hwmon voltage label files
845 createFile("in0_label"); // PAGE 6 -> file number 0
846 createFile("in13_label"); // PAGE 9 -> file number 13
847
848 MockServices services;
849
850 std::string name{"xyz_pseq"};
851 uint8_t bus{3};
852 uint16_t address{0x72};
853 std::string powerControlGPIOName{"power-chassis-control"};
854 std::string powerGoodGPIOName{"power-chassis-good"};
855 std::vector<std::unique_ptr<Rail>> rails;
856 PMBusDriverDevice device{
857 name,
858 bus,
859 address,
860 powerControlGPIOName,
861 powerGoodGPIOName,
862 std::move(rails),
863 services};
864
865 MockPMBus& pmbus = static_cast<MockPMBus&>(device.getPMBusInterface());
866 EXPECT_CALL(pmbus, getPath(Type::Hwmon))
867 .Times(1)
868 .WillOnce(Return(tempDirPath));
869 EXPECT_CALL(pmbus, readString("in0_label", Type::Hwmon))
870 .Times(1)
871 .WillOnce(Return("vout7")); // PAGE number 6 + 1
872 EXPECT_CALL(pmbus, readString("in13_label", Type::Hwmon))
873 .Times(1)
874 .WillOnce(Return("vout10")); // PAGE number 9 + 1
875
876 // Map was empty and needs to be built
877 uint8_t page{6};
878 EXPECT_EQ(device.getFileNumber(page), 0);
879
880 // Map had already been built
881 page = 9;
882 EXPECT_EQ(device.getFileNumber(page), 13);
883 }
884
885 // Test where fails: No mapping for specified PMBus PAGE
886 {
887 // Create simulated hwmon voltage label files
888 createFile("in0_label"); // PAGE 6 -> file number 0
889 createFile("in13_label"); // PAGE 9 -> file number 13
890
891 MockServices services;
892
893 std::string name{"xyz_pseq"};
894 uint8_t bus{3};
895 uint16_t address{0x72};
896 std::string powerControlGPIOName{"power-chassis-control"};
897 std::string powerGoodGPIOName{"power-chassis-good"};
898 std::vector<std::unique_ptr<Rail>> rails;
899 PMBusDriverDevice device{
900 name,
901 bus,
902 address,
903 powerControlGPIOName,
904 powerGoodGPIOName,
905 std::move(rails),
906 services};
907
908 MockPMBus& pmbus = static_cast<MockPMBus&>(device.getPMBusInterface());
909 EXPECT_CALL(pmbus, getPath(Type::Hwmon))
910 .Times(1)
911 .WillOnce(Return(tempDirPath));
912 EXPECT_CALL(pmbus, readString("in0_label", Type::Hwmon))
913 .Times(1)
914 .WillOnce(Return("vout7")); // PAGE number 6 + 1
915 EXPECT_CALL(pmbus, readString("in13_label", Type::Hwmon))
916 .Times(1)
917 .WillOnce(Return("vout10")); // PAGE number 9 + 1
918
919 try
920 {
921 uint8_t page{13};
922 device.getFileNumber(page);
923 ADD_FAILURE() << "Should not have reached this line.";
924 }
925 catch (const std::exception& e)
926 {
927 EXPECT_STREQ(
928 e.what(),
929 "Unable to find hwmon file number for PAGE 13 of device xyz_pseq");
930 }
931 }
932 }
933
TEST_F(PMBusDriverDeviceTests,PrepareForPgoodFaultDetection)934 TEST_F(PMBusDriverDeviceTests, PrepareForPgoodFaultDetection)
935 {
936 // This is a protected method and cannot be called directly from a gtest.
937 // Call findPgoodFault() which calls prepareForPgoodFaultDetection().
938
939 // Create simulated hwmon voltage label file
940 createFile("in1_label"); // PAGE 6 -> file number 1
941
942 MockServices services;
943 std::vector<int> gpioValues{1, 1, 1};
944 EXPECT_CALL(services, getGPIOValues("xyz_pseq"))
945 .Times(1)
946 .WillOnce(Return(gpioValues));
947
948 std::string name{"xyz_pseq"};
949 uint8_t bus{3};
950 uint16_t address{0x72};
951 std::string powerControlGPIOName{"power-chassis-control"};
952 std::string powerGoodGPIOName{"power-chassis-good"};
953 std::vector<std::unique_ptr<Rail>> rails;
954 PMBusDriverDevice device{
955 name,
956 bus,
957 address,
958 powerControlGPIOName,
959 powerGoodGPIOName,
960 std::move(rails),
961 services};
962
963 // Methods that get hwmon file info should be called twice
964 MockPMBus& pmbus = static_cast<MockPMBus&>(device.getPMBusInterface());
965 EXPECT_CALL(pmbus, getPath(Type::Hwmon))
966 .Times(2)
967 .WillRepeatedly(Return(tempDirPath));
968 EXPECT_CALL(pmbus, readString("in1_label", Type::Hwmon))
969 .Times(2)
970 .WillRepeatedly(Return("vout7")); // PAGE number 6 + 1
971
972 // Map was empty and needs to be built
973 uint8_t page{6};
974 EXPECT_EQ(device.getFileNumber(page), 1);
975
976 // Call findPgoodFault() which calls prepareForPgoodFaultDetection() which
977 // rebuilds the map.
978 std::string powerSupplyError{};
979 std::map<std::string, std::string> additionalData{};
980 std::string error =
981 device.findPgoodFault(services, powerSupplyError, additionalData);
982 EXPECT_TRUE(error.empty());
983 EXPECT_EQ(additionalData.size(), 0);
984
985 EXPECT_EQ(device.getFileNumber(page), 1);
986 }
987