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<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 */ 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 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 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 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 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 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 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 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 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 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 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 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 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