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