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 #include "chassis.hpp" 17 #include "config_file_parser.hpp" 18 #include "config_file_parser_error.hpp" 19 #include "mock_services.hpp" 20 #include "power_sequencer_device.hpp" 21 #include "rail.hpp" 22 #include "temporary_file.hpp" 23 #include "temporary_subdirectory.hpp" 24 25 #include <sys/stat.h> // for chmod() 26 27 #include <nlohmann/json.hpp> 28 29 #include <cstdint> 30 #include <exception> 31 #include <filesystem> 32 #include <fstream> 33 #include <map> 34 #include <memory> 35 #include <optional> 36 #include <stdexcept> 37 #include <string> 38 #include <tuple> 39 #include <vector> 40 41 #include <gtest/gtest.h> 42 43 using namespace phosphor::power::sequencer; 44 using namespace phosphor::power::sequencer::config_file_parser; 45 using namespace phosphor::power::sequencer::config_file_parser::internal; 46 using namespace phosphor::power::util; 47 using json = nlohmann::json; 48 namespace fs = std::filesystem; 49 50 void writeConfigFile(const fs::path& pathName, const std::string& contents) 51 { 52 std::ofstream file{pathName}; 53 file << contents; 54 } 55 56 void writeConfigFile(const fs::path& pathName, const json& contents) 57 { 58 std::ofstream file{pathName}; 59 file << contents; 60 } 61 62 TEST(ConfigFileParserTests, Find) 63 { 64 std::vector<std::string> compatibleSystemTypes{ 65 "com.acme.Hardware.Chassis.Model.MegaServer4CPU", 66 "com.acme.Hardware.Chassis.Model.MegaServer", 67 "com.acme.Hardware.Chassis.Model.Server"}; 68 69 // Test where works: Fully qualified system type: First in list 70 { 71 TemporarySubDirectory configFileDir; 72 fs::path configFileDirPath = configFileDir.getPath(); 73 74 fs::path configFilePath = configFileDirPath; 75 configFilePath /= "com.acme.Hardware.Chassis.Model.MegaServer4CPU.json"; 76 writeConfigFile(configFilePath, std::string{""}); 77 78 fs::path pathFound = find(compatibleSystemTypes, configFileDirPath); 79 EXPECT_EQ(pathFound, configFilePath); 80 } 81 82 // Test where works: Fully qualified system type: Second in list 83 { 84 TemporarySubDirectory configFileDir; 85 fs::path configFileDirPath = configFileDir.getPath(); 86 87 fs::path configFilePath = configFileDirPath; 88 configFilePath /= "com.acme.Hardware.Chassis.Model.MegaServer.json"; 89 writeConfigFile(configFilePath, std::string{""}); 90 91 fs::path pathFound = find(compatibleSystemTypes, configFileDirPath); 92 EXPECT_EQ(pathFound, configFilePath); 93 } 94 95 // Test where works: Last node in system type: Second in list 96 { 97 TemporarySubDirectory configFileDir; 98 fs::path configFileDirPath = configFileDir.getPath(); 99 100 fs::path configFilePath = configFileDirPath; 101 configFilePath /= "MegaServer.json"; 102 writeConfigFile(configFilePath, std::string{""}); 103 104 fs::path pathFound = find(compatibleSystemTypes, configFileDirPath); 105 EXPECT_EQ(pathFound, configFilePath); 106 } 107 108 // Test where works: Last node in system type: Last in list 109 { 110 TemporarySubDirectory configFileDir; 111 fs::path configFileDirPath = configFileDir.getPath(); 112 113 fs::path configFilePath = configFileDirPath; 114 configFilePath /= "Server.json"; 115 writeConfigFile(configFilePath, std::string{""}); 116 117 fs::path pathFound = find(compatibleSystemTypes, configFileDirPath); 118 EXPECT_EQ(pathFound, configFilePath); 119 } 120 121 // Test where works: System type has no '.' 122 { 123 TemporarySubDirectory configFileDir; 124 fs::path configFileDirPath = configFileDir.getPath(); 125 126 fs::path configFilePath = configFileDirPath; 127 configFilePath /= "Server.json"; 128 writeConfigFile(configFilePath, std::string{""}); 129 130 std::vector<std::string> noDotSystemTypes{"MegaServer4CPU", 131 "MegaServer", "Server"}; 132 fs::path pathFound = find(noDotSystemTypes, configFileDirPath); 133 EXPECT_EQ(pathFound, configFilePath); 134 } 135 136 // Test where fails: System type list is empty 137 { 138 TemporarySubDirectory configFileDir; 139 fs::path configFileDirPath = configFileDir.getPath(); 140 141 fs::path configFilePath = configFileDirPath; 142 configFilePath /= "Server.json"; 143 writeConfigFile(configFilePath, std::string{""}); 144 145 std::vector<std::string> emptySystemTypes{}; 146 fs::path pathFound = find(emptySystemTypes, configFileDirPath); 147 EXPECT_TRUE(pathFound.empty()); 148 } 149 150 // Test where fails: Configuration file directory is empty 151 { 152 TemporarySubDirectory configFileDir; 153 fs::path configFileDirPath = configFileDir.getPath(); 154 155 fs::path pathFound = find(compatibleSystemTypes, configFileDirPath); 156 EXPECT_TRUE(pathFound.empty()); 157 } 158 159 // Test where fails: Configuration file directory does not exist 160 { 161 fs::path configFileDirPath{"/tmp/does_not_exist_XYZ"}; 162 163 fs::path pathFound = find(compatibleSystemTypes, configFileDirPath); 164 EXPECT_TRUE(pathFound.empty()); 165 } 166 167 // Test where fails: Configuration file directory is not readable 168 { 169 TemporarySubDirectory configFileDir; 170 fs::path configFileDirPath = configFileDir.getPath(); 171 fs::permissions(configFileDirPath, fs::perms::none); 172 173 EXPECT_THROW(find(compatibleSystemTypes, configFileDirPath), 174 std::exception); 175 176 fs::permissions(configFileDirPath, fs::perms::owner_all); 177 } 178 179 // Test where fails: No matching file name found 180 { 181 TemporarySubDirectory configFileDir; 182 fs::path configFileDirPath = configFileDir.getPath(); 183 184 fs::path configFilePath = configFileDirPath; 185 configFilePath /= "com.acme.Hardware.Chassis.Model.MegaServer"; 186 writeConfigFile(configFilePath, std::string{""}); 187 188 fs::path pathFound = find(compatibleSystemTypes, configFileDirPath); 189 EXPECT_TRUE(pathFound.empty()); 190 } 191 192 // Test where fails: Matching file name is a directory: Fully qualified 193 { 194 TemporarySubDirectory configFileDir; 195 fs::path configFileDirPath = configFileDir.getPath(); 196 197 fs::path configFilePath = configFileDirPath; 198 configFilePath /= "com.acme.Hardware.Chassis.Model.MegaServer4CPU.json"; 199 fs::create_directory(configFilePath); 200 201 fs::path pathFound = find(compatibleSystemTypes, configFileDirPath); 202 EXPECT_TRUE(pathFound.empty()); 203 } 204 205 // Test where fails: Matching file name is a directory: Last node 206 { 207 TemporarySubDirectory configFileDir; 208 fs::path configFileDirPath = configFileDir.getPath(); 209 210 fs::path configFilePath = configFileDirPath; 211 configFilePath /= "MegaServer.json"; 212 fs::create_directory(configFilePath); 213 214 fs::path pathFound = find(compatibleSystemTypes, configFileDirPath); 215 EXPECT_TRUE(pathFound.empty()); 216 } 217 218 // Test where fails: System type has no '.' 219 { 220 TemporarySubDirectory configFileDir; 221 fs::path configFileDirPath = configFileDir.getPath(); 222 223 fs::path configFilePath = configFileDirPath; 224 configFilePath /= "MegaServer2CPU.json"; 225 writeConfigFile(configFilePath, std::string{""}); 226 227 std::vector<std::string> noDotSystemTypes{"MegaServer4CPU", 228 "MegaServer", "Server", ""}; 229 fs::path pathFound = find(noDotSystemTypes, configFileDirPath); 230 EXPECT_TRUE(pathFound.empty()); 231 } 232 233 // Test where fails: System type ends with '.' 234 { 235 TemporarySubDirectory configFileDir; 236 fs::path configFileDirPath = configFileDir.getPath(); 237 238 fs::path configFilePath = configFileDirPath; 239 configFilePath /= "MegaServer4CPU.json"; 240 writeConfigFile(configFilePath, std::string{""}); 241 242 std::vector<std::string> dotAtEndSystemTypes{ 243 "com.acme.Hardware.Chassis.Model.MegaServer4CPU.", "a.", "."}; 244 fs::path pathFound = find(dotAtEndSystemTypes, configFileDirPath); 245 EXPECT_TRUE(pathFound.empty()); 246 } 247 } 248 249 TEST(ConfigFileParserTests, Parse) 250 { 251 // Test where works 252 { 253 const json configFileContents = R"( 254 { 255 "chassis": [ 256 { 257 "number": 1, 258 "inventory_path": "/xyz/openbmc_project/inventory/system/chassis1", 259 "power_sequencers": [] 260 }, 261 { 262 "number": 2, 263 "inventory_path": "/xyz/openbmc_project/inventory/system/chassis2", 264 "power_sequencers": [] 265 } 266 ] 267 } 268 )"_json; 269 270 TemporaryFile configFile; 271 fs::path pathName{configFile.getPath()}; 272 writeConfigFile(pathName, configFileContents); 273 274 MockServices services{}; 275 auto chassis = parse(pathName, services); 276 277 EXPECT_EQ(chassis.size(), 2); 278 EXPECT_EQ(chassis[0]->getNumber(), 1); 279 EXPECT_EQ(chassis[0]->getInventoryPath(), 280 "/xyz/openbmc_project/inventory/system/chassis1"); 281 EXPECT_EQ(chassis[1]->getNumber(), 2); 282 EXPECT_EQ(chassis[1]->getInventoryPath(), 283 "/xyz/openbmc_project/inventory/system/chassis2"); 284 } 285 286 // Test where fails: File does not exist 287 { 288 fs::path pathName{"/tmp/non_existent_file"}; 289 MockServices services{}; 290 EXPECT_THROW(parse(pathName, services), ConfigFileParserError); 291 } 292 293 // Test where fails: File is not readable 294 { 295 const json configFileContents = R"( 296 { 297 "chassis": [ 298 { 299 "number": 1, 300 "inventory_path": "/xyz/openbmc_project/inventory/system/chassis1", 301 "power_sequencers": [] 302 } 303 ] 304 } 305 )"_json; 306 307 TemporaryFile configFile; 308 fs::path pathName{configFile.getPath()}; 309 writeConfigFile(pathName, configFileContents); 310 311 chmod(pathName.c_str(), 0222); 312 MockServices services{}; 313 EXPECT_THROW(parse(pathName, services), ConfigFileParserError); 314 } 315 316 // Test where fails: File is not valid JSON 317 { 318 const std::string configFileContents = "] foo ["; 319 320 TemporaryFile configFile; 321 fs::path pathName{configFile.getPath()}; 322 writeConfigFile(pathName, configFileContents); 323 324 MockServices services{}; 325 EXPECT_THROW(parse(pathName, services), ConfigFileParserError); 326 } 327 328 // Test where fails: JSON does not conform to config file format 329 { 330 const json configFileContents = R"( [ "foo", "bar" ] )"_json; 331 332 TemporaryFile configFile; 333 fs::path pathName{configFile.getPath()}; 334 writeConfigFile(pathName, configFileContents); 335 336 MockServices services{}; 337 EXPECT_THROW(parse(pathName, services), ConfigFileParserError); 338 } 339 } 340 341 TEST(ConfigFileParserTests, ParseChassis) 342 { 343 // Constants used by multiple tests 344 const json templateElement = R"( 345 { 346 "id": "foo_chassis", 347 "number": "${chassis_number}", 348 "inventory_path": "/xyz/openbmc_project/inventory/system/chassis${chassis_number}", 349 "power_sequencers": [ 350 { 351 "type": "UCD90320", 352 "i2c_interface": { "bus": "${bus}", "address": "${address}" }, 353 "power_control_gpio_name": "power-chassis${chassis_number}-control", 354 "power_good_gpio_name": "power-chassis${chassis_number}-good", 355 "rails": [] 356 } 357 ] 358 } 359 )"_json; 360 const std::map<std::string, JSONRefWrapper> chassisTemplates{ 361 {"foo_chassis", templateElement}}; 362 363 // Test where works: Does not use a template 364 { 365 const json element = R"( 366 { 367 "number": 1, 368 "inventory_path": "/xyz/openbmc_project/inventory/system/chassis", 369 "power_sequencers": [ 370 { 371 "type": "UCD90320", 372 "i2c_interface": { "bus": 3, "address": "0x11" }, 373 "power_control_gpio_name": "power-chassis-control", 374 "power_good_gpio_name": "power-chassis-good", 375 "rails": [] 376 } 377 ] 378 } 379 )"_json; 380 MockServices services{}; 381 auto chassis = parseChassis(element, chassisTemplates, services); 382 EXPECT_EQ(chassis->getNumber(), 1); 383 EXPECT_EQ(chassis->getInventoryPath(), 384 "/xyz/openbmc_project/inventory/system/chassis"); 385 EXPECT_EQ(chassis->getPowerSequencers().size(), 1); 386 EXPECT_EQ(chassis->getPowerSequencers()[0]->getName(), "UCD90320"); 387 EXPECT_EQ(chassis->getPowerSequencers()[0]->getBus(), 3); 388 EXPECT_EQ(chassis->getPowerSequencers()[0]->getAddress(), 0x11); 389 } 390 391 // Test where works: Uses template: No comments specified 392 { 393 const json element = R"( 394 { 395 "template_id": "foo_chassis", 396 "template_variable_values": { 397 "chassis_number": "2", 398 "bus": "13", 399 "address": "0x70" 400 } 401 } 402 )"_json; 403 MockServices services{}; 404 auto chassis = parseChassis(element, chassisTemplates, services); 405 EXPECT_EQ(chassis->getNumber(), 2); 406 EXPECT_EQ(chassis->getInventoryPath(), 407 "/xyz/openbmc_project/inventory/system/chassis2"); 408 EXPECT_EQ(chassis->getPowerSequencers().size(), 1); 409 EXPECT_EQ(chassis->getPowerSequencers()[0]->getName(), "UCD90320"); 410 EXPECT_EQ(chassis->getPowerSequencers()[0]->getBus(), 13); 411 EXPECT_EQ(chassis->getPowerSequencers()[0]->getAddress(), 0x70); 412 } 413 414 // Test where works: Uses template: Comments specified 415 { 416 const json element = R"( 417 { 418 "comments": ["Chassis 3: Standard hardware layout"], 419 "template_id": "foo_chassis", 420 "template_variable_values": { 421 "chassis_number": "3", 422 "bus": "23", 423 "address": "0x54" 424 } 425 } 426 )"_json; 427 MockServices services{}; 428 auto chassis = parseChassis(element, chassisTemplates, services); 429 EXPECT_EQ(chassis->getNumber(), 3); 430 EXPECT_EQ(chassis->getInventoryPath(), 431 "/xyz/openbmc_project/inventory/system/chassis3"); 432 EXPECT_EQ(chassis->getPowerSequencers().size(), 1); 433 EXPECT_EQ(chassis->getPowerSequencers()[0]->getName(), "UCD90320"); 434 EXPECT_EQ(chassis->getPowerSequencers()[0]->getBus(), 23); 435 EXPECT_EQ(chassis->getPowerSequencers()[0]->getAddress(), 0x54); 436 } 437 438 // Test where fails: Element is not an object 439 try 440 { 441 const json element = R"( [ "vdda", "vddb" ] )"_json; 442 MockServices services{}; 443 parseChassis(element, chassisTemplates, services); 444 ADD_FAILURE() << "Should not have reached this line."; 445 } 446 catch (const std::invalid_argument& e) 447 { 448 EXPECT_STREQ(e.what(), "Element is not an object"); 449 } 450 451 // Test where fails: Does not use a template: Cannot parse properties 452 try 453 { 454 const json element = R"( 455 { 456 "number": "one", 457 "inventory_path": "/xyz/openbmc_project/inventory/system/chassis", 458 "power_sequencers": [] 459 } 460 )"_json; 461 MockServices services{}; 462 parseChassis(element, chassisTemplates, services); 463 ADD_FAILURE() << "Should not have reached this line."; 464 } 465 catch (const std::invalid_argument& e) 466 { 467 EXPECT_STREQ(e.what(), "Element is not an integer"); 468 } 469 470 // Test where fails: Uses template: Required template_variable_values 471 // property not specified 472 try 473 { 474 const json element = R"( 475 { 476 "template_id": "foo_chassis" 477 } 478 )"_json; 479 MockServices services{}; 480 parseChassis(element, chassisTemplates, services); 481 ADD_FAILURE() << "Should not have reached this line."; 482 } 483 catch (const std::invalid_argument& e) 484 { 485 EXPECT_STREQ(e.what(), 486 "Required property missing: template_variable_values"); 487 } 488 489 // Test where fails: Uses template: template_id value is invalid: Not a 490 // string 491 try 492 { 493 const json element = R"( 494 { 495 "template_id": 23, 496 "template_variable_values": { "chassis_number": "2" } 497 } 498 )"_json; 499 MockServices services{}; 500 parseChassis(element, chassisTemplates, services); 501 ADD_FAILURE() << "Should not have reached this line."; 502 } 503 catch (const std::invalid_argument& e) 504 { 505 EXPECT_STREQ(e.what(), "Element is not a string"); 506 } 507 508 // Test where fails: Uses template: template_id value is invalid: No 509 // matching template 510 try 511 { 512 const json element = R"( 513 { 514 "template_id": "does_not_exist", 515 "template_variable_values": { "chassis_number": "2" } 516 } 517 )"_json; 518 MockServices services{}; 519 parseChassis(element, chassisTemplates, services); 520 ADD_FAILURE() << "Should not have reached this line."; 521 } 522 catch (const std::invalid_argument& e) 523 { 524 EXPECT_STREQ(e.what(), "Invalid chassis template id: does_not_exist"); 525 } 526 527 // Test where fails: Uses template: template_variable_values value is 528 // invalid 529 try 530 { 531 const json element = R"( 532 { 533 "template_id": "foo_chassis", 534 "template_variable_values": { "chassis_number": 2 } 535 } 536 )"_json; 537 MockServices services{}; 538 parseChassis(element, chassisTemplates, services); 539 ADD_FAILURE() << "Should not have reached this line."; 540 } 541 catch (const std::invalid_argument& e) 542 { 543 EXPECT_STREQ(e.what(), "Element is not a string"); 544 } 545 546 // Test where fails: Uses template: Invalid property specified 547 try 548 { 549 const json element = R"( 550 { 551 "template_id": "foo_chassis", 552 "template_variable_values": { "chassis_number": "2" }, 553 "foo": "bar" 554 } 555 )"_json; 556 MockServices services{}; 557 parseChassis(element, chassisTemplates, services); 558 ADD_FAILURE() << "Should not have reached this line."; 559 } 560 catch (const std::invalid_argument& e) 561 { 562 EXPECT_STREQ(e.what(), "Element contains an invalid property"); 563 } 564 565 // Test where fails: Uses template: Cannot parse properties in template 566 try 567 { 568 const json element = R"( 569 { 570 "template_id": "foo_chassis", 571 "template_variable_values": { "chassis_number": "0" } 572 } 573 )"_json; 574 MockServices services{}; 575 parseChassis(element, chassisTemplates, services); 576 ADD_FAILURE() << "Should not have reached this line."; 577 } 578 catch (const std::invalid_argument& e) 579 { 580 EXPECT_STREQ(e.what(), "Invalid chassis number: Must be > 0"); 581 } 582 } 583 584 TEST(ConfigFileParserTests, ParseChassisArray) 585 { 586 // Constants used by multiple tests 587 const json fooTemplateElement = R"( 588 { 589 "id": "foo_chassis", 590 "number": "${chassis_number}", 591 "inventory_path": "/xyz/openbmc_project/inventory/system/chassis${chassis_number}", 592 "power_sequencers": [] 593 } 594 )"_json; 595 const json barTemplateElement = R"( 596 { 597 "id": "bar_chassis", 598 "number": "${chassis_number}", 599 "inventory_path": "/xyz/openbmc_project/inventory/system/bar_chassis${chassis_number}", 600 "power_sequencers": [] 601 } 602 )"_json; 603 const std::map<std::string, JSONRefWrapper> chassisTemplates{ 604 {"foo_chassis", fooTemplateElement}, 605 {"bar_chassis", barTemplateElement}}; 606 607 // Test where works: Array is empty 608 { 609 const json element = R"( 610 [ 611 ] 612 )"_json; 613 MockServices services{}; 614 auto chassis = parseChassisArray(element, chassisTemplates, services); 615 EXPECT_EQ(chassis.size(), 0); 616 } 617 618 // Test where works: Template not used 619 { 620 const json element = R"( 621 [ 622 { 623 "number": 1, 624 "inventory_path": "/xyz/openbmc_project/inventory/system/chassis1", 625 "power_sequencers": [] 626 }, 627 { 628 "number": 2, 629 "inventory_path": "/xyz/openbmc_project/inventory/system/chassis2", 630 "power_sequencers": [] 631 } 632 ] 633 )"_json; 634 MockServices services{}; 635 auto chassis = parseChassisArray(element, chassisTemplates, services); 636 EXPECT_EQ(chassis.size(), 2); 637 EXPECT_EQ(chassis[0]->getNumber(), 1); 638 EXPECT_EQ(chassis[0]->getInventoryPath(), 639 "/xyz/openbmc_project/inventory/system/chassis1"); 640 EXPECT_EQ(chassis[1]->getNumber(), 2); 641 EXPECT_EQ(chassis[1]->getInventoryPath(), 642 "/xyz/openbmc_project/inventory/system/chassis2"); 643 } 644 645 // Test where works: Template used 646 { 647 const json element = R"( 648 [ 649 { 650 "template_id": "foo_chassis", 651 "template_variable_values": { "chassis_number": "2" } 652 }, 653 { 654 "template_id": "bar_chassis", 655 "template_variable_values": { "chassis_number": "3" } 656 } 657 ] 658 )"_json; 659 MockServices services{}; 660 auto chassis = parseChassisArray(element, chassisTemplates, services); 661 EXPECT_EQ(chassis.size(), 2); 662 EXPECT_EQ(chassis[0]->getNumber(), 2); 663 EXPECT_EQ(chassis[0]->getInventoryPath(), 664 "/xyz/openbmc_project/inventory/system/chassis2"); 665 EXPECT_EQ(chassis[1]->getNumber(), 3); 666 EXPECT_EQ(chassis[1]->getInventoryPath(), 667 "/xyz/openbmc_project/inventory/system/bar_chassis3"); 668 } 669 670 // Test where fails: Element is not an array 671 try 672 { 673 const json element = R"( 674 { 675 "foo": "bar" 676 } 677 )"_json; 678 MockServices services{}; 679 parseChassisArray(element, chassisTemplates, services); 680 ADD_FAILURE() << "Should not have reached this line."; 681 } 682 catch (const std::invalid_argument& e) 683 { 684 EXPECT_STREQ(e.what(), "Element is not an array"); 685 } 686 687 // Test where fails: Element within array is invalid 688 try 689 { 690 const json element = R"( 691 [ 692 { 693 "number": true, 694 "inventory_path": "/xyz/openbmc_project/inventory/system/chassis", 695 "power_sequencers": [] 696 } 697 ] 698 )"_json; 699 MockServices services{}; 700 parseChassisArray(element, chassisTemplates, services); 701 ADD_FAILURE() << "Should not have reached this line."; 702 } 703 catch (const std::invalid_argument& e) 704 { 705 EXPECT_STREQ(e.what(), "Element is not an integer"); 706 } 707 708 // Test where fails: Invalid variable value specified 709 try 710 { 711 const json element = R"( 712 [ 713 { 714 "template_id": "foo_chassis", 715 "template_variable_values": { "chassis_number": "two" } 716 } 717 ] 718 )"_json; 719 MockServices services{}; 720 parseChassisArray(element, chassisTemplates, services); 721 ADD_FAILURE() << "Should not have reached this line."; 722 } 723 catch (const std::invalid_argument& e) 724 { 725 EXPECT_STREQ(e.what(), "Element is not an integer"); 726 } 727 } 728 729 TEST(ConfigFileParserTests, ParseChassisProperties) 730 { 731 // Test where works: Parse chassis object without template/variables: Has 732 // comments property 733 { 734 const json element = R"( 735 { 736 "comments": [ "Chassis 1: Has all CPUs, fans, and PSUs" ], 737 "number": 1, 738 "inventory_path": "/xyz/openbmc_project/inventory/system/chassis", 739 "power_sequencers": [ 740 { 741 "type": "UCD90160", 742 "i2c_interface": { "bus": 3, "address": "0x11" }, 743 "power_control_gpio_name": "power-chassis-control", 744 "power_good_gpio_name": "power-chassis-good", 745 "rails": [ { "name": "VDD_CPU0" }, { "name": "VCS_CPU1" } ] 746 } 747 ] 748 } 749 )"_json; 750 bool isChassisTemplate{false}; 751 std::map<std::string, std::string> variables{}; 752 MockServices services{}; 753 auto chassis = parseChassisProperties(element, isChassisTemplate, 754 variables, services); 755 EXPECT_EQ(chassis->getNumber(), 1); 756 EXPECT_EQ(chassis->getInventoryPath(), 757 "/xyz/openbmc_project/inventory/system/chassis"); 758 EXPECT_EQ(chassis->getPowerSequencers().size(), 1); 759 EXPECT_EQ(chassis->getPowerSequencers()[0]->getName(), "UCD90160"); 760 EXPECT_EQ(chassis->getPowerSequencers()[0]->getBus(), 3); 761 EXPECT_EQ(chassis->getPowerSequencers()[0]->getAddress(), 0x11); 762 EXPECT_EQ(chassis->getPowerSequencers()[0]->getPowerControlGPIOName(), 763 "power-chassis-control"); 764 EXPECT_EQ(chassis->getPowerSequencers()[0]->getPowerGoodGPIOName(), 765 "power-chassis-good"); 766 EXPECT_EQ(chassis->getPowerSequencers()[0]->getRails().size(), 2); 767 EXPECT_EQ(chassis->getPowerSequencers()[0]->getRails()[0]->getName(), 768 "VDD_CPU0"); 769 EXPECT_EQ(chassis->getPowerSequencers()[0]->getRails()[1]->getName(), 770 "VCS_CPU1"); 771 } 772 773 // Test where works: Parse chassis_template object with variables: No 774 // comments property 775 { 776 const json element = R"( 777 { 778 "id": "foo_chassis", 779 "number": "${chassis_number}", 780 "inventory_path": "/xyz/openbmc_project/inventory/system/chassis${chassis_number}", 781 "power_sequencers": [ 782 { 783 "type": "UCD90320", 784 "i2c_interface": { "bus": "${bus}", "address": "${address}" }, 785 "power_control_gpio_name": "power-chassis${chassis_number}-control", 786 "power_good_gpio_name": "power-chassis${chassis_number}-good", 787 "rails": [ { "name": "vio${chassis_number}" } ] 788 } 789 ] 790 } 791 )"_json; 792 bool isChassisTemplate{true}; 793 std::map<std::string, std::string> variables{ 794 {"chassis_number", "2"}, {"bus", "12"}, {"address", "0x71"}}; 795 MockServices services{}; 796 auto chassis = parseChassisProperties(element, isChassisTemplate, 797 variables, services); 798 EXPECT_EQ(chassis->getNumber(), 2); 799 EXPECT_EQ(chassis->getInventoryPath(), 800 "/xyz/openbmc_project/inventory/system/chassis2"); 801 EXPECT_EQ(chassis->getPowerSequencers().size(), 1); 802 EXPECT_EQ(chassis->getPowerSequencers()[0]->getName(), "UCD90320"); 803 EXPECT_EQ(chassis->getPowerSequencers()[0]->getBus(), 12); 804 EXPECT_EQ(chassis->getPowerSequencers()[0]->getAddress(), 0x71); 805 EXPECT_EQ(chassis->getPowerSequencers()[0]->getPowerControlGPIOName(), 806 "power-chassis2-control"); 807 EXPECT_EQ(chassis->getPowerSequencers()[0]->getPowerGoodGPIOName(), 808 "power-chassis2-good"); 809 EXPECT_EQ(chassis->getPowerSequencers()[0]->getRails().size(), 1); 810 EXPECT_EQ(chassis->getPowerSequencers()[0]->getRails()[0]->getName(), 811 "vio2"); 812 } 813 814 // Test where fails: Element is not an object 815 try 816 { 817 const json element = R"( true )"_json; 818 bool isChassisTemplate{false}; 819 std::map<std::string, std::string> variables{}; 820 MockServices services{}; 821 parseChassisProperties(element, isChassisTemplate, variables, services); 822 ADD_FAILURE() << "Should not have reached this line."; 823 } 824 catch (const std::invalid_argument& e) 825 { 826 EXPECT_STREQ(e.what(), "Element is not an object"); 827 } 828 829 // Test where fails: Required id property not specified in chassis template 830 try 831 { 832 const json element = R"( 833 { 834 "number": "${chassis_number}", 835 "inventory_path": "/xyz/openbmc_project/inventory/system/chassis${chassis_number}", 836 "power_sequencers": [] 837 } 838 )"_json; 839 bool isChassisTemplate{true}; 840 std::map<std::string, std::string> variables{{"chassis_number", "2"}}; 841 MockServices services{}; 842 parseChassisProperties(element, isChassisTemplate, variables, services); 843 ADD_FAILURE() << "Should not have reached this line."; 844 } 845 catch (const std::invalid_argument& e) 846 { 847 EXPECT_STREQ(e.what(), "Required property missing: id"); 848 } 849 850 // Test where fails: Required number property not specified 851 try 852 { 853 const json element = R"( 854 { 855 "inventory_path": "/xyz/openbmc_project/inventory/system/chassis", 856 "power_sequencers": [] 857 } 858 )"_json; 859 bool isChassisTemplate{false}; 860 std::map<std::string, std::string> variables{}; 861 MockServices services{}; 862 parseChassisProperties(element, isChassisTemplate, variables, services); 863 ADD_FAILURE() << "Should not have reached this line."; 864 } 865 catch (const std::invalid_argument& e) 866 { 867 EXPECT_STREQ(e.what(), "Required property missing: number"); 868 } 869 870 // Test where fails: Required inventory_path property not specified 871 try 872 { 873 const json element = R"( 874 { 875 "id": "foo_chassis", 876 "number": "${chassis_number}", 877 "power_sequencers": [] 878 } 879 )"_json; 880 bool isChassisTemplate{true}; 881 std::map<std::string, std::string> variables{{"chassis_number", "2"}}; 882 MockServices services{}; 883 parseChassisProperties(element, isChassisTemplate, variables, services); 884 ADD_FAILURE() << "Should not have reached this line."; 885 } 886 catch (const std::invalid_argument& e) 887 { 888 EXPECT_STREQ(e.what(), "Required property missing: inventory_path"); 889 } 890 891 // Test where fails: Required power_sequencers property not specified 892 try 893 { 894 const json element = R"( 895 { 896 "number": 1, 897 "inventory_path": "/xyz/openbmc_project/inventory/system/chassis" 898 } 899 )"_json; 900 bool isChassisTemplate{false}; 901 std::map<std::string, std::string> variables{}; 902 MockServices services{}; 903 parseChassisProperties(element, isChassisTemplate, variables, services); 904 ADD_FAILURE() << "Should not have reached this line."; 905 } 906 catch (const std::invalid_argument& e) 907 { 908 EXPECT_STREQ(e.what(), "Required property missing: power_sequencers"); 909 } 910 911 // Test where fails: number value is invalid: Not an integer 912 try 913 { 914 const json element = R"( 915 { 916 "number": "two", 917 "inventory_path": "/xyz/openbmc_project/inventory/system/chassis", 918 "power_sequencers": [] 919 } 920 )"_json; 921 bool isChassisTemplate{false}; 922 std::map<std::string, std::string> variables{}; 923 MockServices services{}; 924 parseChassisProperties(element, isChassisTemplate, variables, services); 925 ADD_FAILURE() << "Should not have reached this line."; 926 } 927 catch (const std::invalid_argument& e) 928 { 929 EXPECT_STREQ(e.what(), "Element is not an integer"); 930 } 931 932 // Test where fails: number value is invalid: Equal to 0 933 try 934 { 935 const json element = R"( 936 { 937 "number": 0, 938 "inventory_path": "/xyz/openbmc_project/inventory/system/chassis", 939 "power_sequencers": [] 940 } 941 )"_json; 942 bool isChassisTemplate{false}; 943 std::map<std::string, std::string> variables{}; 944 MockServices services{}; 945 parseChassisProperties(element, isChassisTemplate, variables, services); 946 ADD_FAILURE() << "Should not have reached this line."; 947 } 948 catch (const std::invalid_argument& e) 949 { 950 EXPECT_STREQ(e.what(), "Invalid chassis number: Must be > 0"); 951 } 952 953 // Test where fails: inventory_path value is invalid 954 try 955 { 956 const json element = R"( 957 { 958 "number": 1, 959 "inventory_path": "", 960 "power_sequencers": [] 961 } 962 )"_json; 963 bool isChassisTemplate{false}; 964 std::map<std::string, std::string> variables{}; 965 MockServices services{}; 966 parseChassisProperties(element, isChassisTemplate, variables, services); 967 ADD_FAILURE() << "Should not have reached this line."; 968 } 969 catch (const std::invalid_argument& e) 970 { 971 EXPECT_STREQ(e.what(), "Element contains an empty string"); 972 } 973 974 // Test where fails: power_sequencers value is invalid 975 try 976 { 977 const json element = R"( 978 { 979 "number": 1, 980 "inventory_path": "/xyz/openbmc_project/inventory/system/chassis", 981 "power_sequencers": { "name": "foo" } 982 } 983 )"_json; 984 bool isChassisTemplate{false}; 985 std::map<std::string, std::string> variables{}; 986 MockServices services{}; 987 parseChassisProperties(element, isChassisTemplate, variables, services); 988 ADD_FAILURE() << "Should not have reached this line."; 989 } 990 catch (const std::invalid_argument& e) 991 { 992 EXPECT_STREQ(e.what(), "Element is not an array"); 993 } 994 995 // Test where fails: Invalid property specified 996 try 997 { 998 const json element = R"( 999 { 1000 "foo": "bar", 1001 "number": 1, 1002 "inventory_path": "/xyz/openbmc_project/inventory/system/chassis", 1003 "power_sequencers": [] 1004 } 1005 )"_json; 1006 bool isChassisTemplate{false}; 1007 std::map<std::string, std::string> variables{}; 1008 MockServices services{}; 1009 parseChassisProperties(element, isChassisTemplate, variables, services); 1010 ADD_FAILURE() << "Should not have reached this line."; 1011 } 1012 catch (const std::invalid_argument& e) 1013 { 1014 EXPECT_STREQ(e.what(), "Element contains an invalid property"); 1015 } 1016 1017 // Test where fails: Invalid variable value specified 1018 try 1019 { 1020 const json element = R"( 1021 { 1022 "id": "foo_chassis", 1023 "number": "${chassis_number}", 1024 "inventory_path": "/xyz/openbmc_project/inventory/system/chassis${chassis_number}", 1025 "power_sequencers": [] 1026 } 1027 )"_json; 1028 bool isChassisTemplate{true}; 1029 std::map<std::string, std::string> variables{{"chassis_number", "two"}}; 1030 MockServices services{}; 1031 parseChassisProperties(element, isChassisTemplate, variables, services); 1032 ADD_FAILURE() << "Should not have reached this line."; 1033 } 1034 catch (const std::invalid_argument& e) 1035 { 1036 EXPECT_STREQ(e.what(), "Element is not an integer"); 1037 } 1038 } 1039 1040 TEST(ConfigFileParserTests, ParseChassisTemplate) 1041 { 1042 // Test where works: comments specified 1043 { 1044 const json element = R"( 1045 { 1046 "comments": [ "This is a template for the foo chassis type", 1047 "Chassis contains a UCD90320 power sequencer" ], 1048 "id": "foo_chassis", 1049 "number": "${chassis_number}", 1050 "inventory_path": "/xyz/openbmc_project/inventory/system/chassis${chassis_number}", 1051 "power_sequencers": [ 1052 { 1053 "type": "UCD90320", 1054 "i2c_interface": { "bus": "${bus}", "address": "0x11" }, 1055 "power_control_gpio_name": "power-chassis${chassis_number}-control", 1056 "power_good_gpio_name": "power-chassis${chassis_number}-good", 1057 "rails": [ { "name": "VDD_CPU0" }, { "name": "VCS_CPU1" } ] 1058 } 1059 ] 1060 } 1061 )"_json; 1062 auto [id, jsonRef] = parseChassisTemplate(element); 1063 EXPECT_EQ(id, "foo_chassis"); 1064 EXPECT_EQ(jsonRef.get()["number"], "${chassis_number}"); 1065 EXPECT_EQ(jsonRef.get()["power_sequencers"].size(), 1); 1066 EXPECT_EQ(jsonRef.get()["power_sequencers"][0]["type"], "UCD90320"); 1067 } 1068 1069 // Test where works: comments not specified 1070 { 1071 const json element = R"( 1072 { 1073 "id": "foo_chassis", 1074 "number": "${chassis_number}", 1075 "inventory_path": "/xyz/openbmc_project/inventory/system/chassis${chassis_number}", 1076 "power_sequencers": [] 1077 } 1078 )"_json; 1079 auto [id, jsonRef] = parseChassisTemplate(element); 1080 EXPECT_EQ(id, "foo_chassis"); 1081 EXPECT_EQ(jsonRef.get()["number"], "${chassis_number}"); 1082 EXPECT_EQ( 1083 jsonRef.get()["inventory_path"], 1084 "/xyz/openbmc_project/inventory/system/chassis${chassis_number}"); 1085 EXPECT_EQ(jsonRef.get()["power_sequencers"].size(), 0); 1086 } 1087 1088 // Test where fails: Element is not an object 1089 try 1090 { 1091 const json element = R"( [ "vdda", "vddb" ] )"_json; 1092 parseChassisTemplate(element); 1093 ADD_FAILURE() << "Should not have reached this line."; 1094 } 1095 catch (const std::invalid_argument& e) 1096 { 1097 EXPECT_STREQ(e.what(), "Element is not an object"); 1098 } 1099 1100 // Test where fails: Required id property not specified 1101 try 1102 { 1103 const json element = R"( 1104 { 1105 "number": "${chassis_number}", 1106 "inventory_path": "/xyz/openbmc_project/inventory/system/chassis${chassis_number}", 1107 "power_sequencers": [] 1108 } 1109 )"_json; 1110 parseChassisTemplate(element); 1111 ADD_FAILURE() << "Should not have reached this line."; 1112 } 1113 catch (const std::invalid_argument& e) 1114 { 1115 EXPECT_STREQ(e.what(), "Required property missing: id"); 1116 } 1117 1118 // Test where fails: Required number property not specified 1119 try 1120 { 1121 const json element = R"( 1122 { 1123 "id": "foo_chassis", 1124 "inventory_path": "/xyz/openbmc_project/inventory/system/chassis${chassis_number}", 1125 "power_sequencers": [] 1126 } 1127 )"_json; 1128 parseChassisTemplate(element); 1129 ADD_FAILURE() << "Should not have reached this line."; 1130 } 1131 catch (const std::invalid_argument& e) 1132 { 1133 EXPECT_STREQ(e.what(), "Required property missing: number"); 1134 } 1135 1136 // Test where fails: Required inventory_path property not specified 1137 try 1138 { 1139 const json element = R"( 1140 { 1141 "id": "foo_chassis", 1142 "number": "${chassis_number}", 1143 "power_sequencers": [] 1144 } 1145 )"_json; 1146 parseChassisTemplate(element); 1147 ADD_FAILURE() << "Should not have reached this line."; 1148 } 1149 catch (const std::invalid_argument& e) 1150 { 1151 EXPECT_STREQ(e.what(), "Required property missing: inventory_path"); 1152 } 1153 1154 // Test where fails: Required power_sequencers property not specified 1155 try 1156 { 1157 const json element = R"( 1158 { 1159 "id": "foo_chassis", 1160 "number": "${chassis_number}", 1161 "inventory_path": "/xyz/openbmc_project/inventory/system/chassis${chassis_number}" 1162 } 1163 )"_json; 1164 parseChassisTemplate(element); 1165 ADD_FAILURE() << "Should not have reached this line."; 1166 } 1167 catch (const std::invalid_argument& e) 1168 { 1169 EXPECT_STREQ(e.what(), "Required property missing: power_sequencers"); 1170 } 1171 1172 // Test where fails: id value is invalid 1173 try 1174 { 1175 const json element = R"( 1176 { 1177 "id": 13, 1178 "number": "${chassis_number}", 1179 "inventory_path": "/xyz/openbmc_project/inventory/system/chassis${chassis_number}", 1180 "power_sequencers": [] 1181 } 1182 )"_json; 1183 parseChassisTemplate(element); 1184 ADD_FAILURE() << "Should not have reached this line."; 1185 } 1186 catch (const std::invalid_argument& e) 1187 { 1188 EXPECT_STREQ(e.what(), "Element is not a string"); 1189 } 1190 1191 // Test where fails: Invalid property specified 1192 try 1193 { 1194 const json element = R"( 1195 { 1196 "id": "foo_chassis", 1197 "number": "${chassis_number}", 1198 "inventory_path": "/xyz/openbmc_project/inventory/system/chassis${chassis_number}", 1199 "power_sequencers": [], 1200 "foo": "bar" 1201 } 1202 )"_json; 1203 parseChassisTemplate(element); 1204 ADD_FAILURE() << "Should not have reached this line."; 1205 } 1206 catch (const std::invalid_argument& e) 1207 { 1208 EXPECT_STREQ(e.what(), "Element contains an invalid property"); 1209 } 1210 } 1211 1212 TEST(ConfigFileParserTests, ParseChassisTemplateArray) 1213 { 1214 // Test where works: Array is empty 1215 { 1216 const json element = R"( 1217 [ 1218 ] 1219 )"_json; 1220 auto chassisTemplates = parseChassisTemplateArray(element); 1221 EXPECT_EQ(chassisTemplates.size(), 0); 1222 } 1223 1224 // Test where works: Array is not empty 1225 { 1226 const json element = R"( 1227 [ 1228 { 1229 "id": "foo_chassis", 1230 "number": "${chassis_number}", 1231 "inventory_path": "/xyz/openbmc_project/inventory/system/chassis${chassis_number}", 1232 "power_sequencers": [] 1233 }, 1234 { 1235 "id": "bar_chassis", 1236 "number": "${number}", 1237 "inventory_path": "/xyz/openbmc_project/inventory/system/bar_chassis${number}", 1238 "power_sequencers": [] 1239 } 1240 ] 1241 )"_json; 1242 auto chassisTemplates = parseChassisTemplateArray(element); 1243 EXPECT_EQ(chassisTemplates.size(), 2); 1244 EXPECT_EQ(chassisTemplates.at("foo_chassis").get()["number"], 1245 "${chassis_number}"); 1246 EXPECT_EQ( 1247 chassisTemplates.at("foo_chassis").get()["inventory_path"], 1248 "/xyz/openbmc_project/inventory/system/chassis${chassis_number}"); 1249 EXPECT_EQ(chassisTemplates.at("bar_chassis").get()["number"], 1250 "${number}"); 1251 EXPECT_EQ(chassisTemplates.at("bar_chassis").get()["inventory_path"], 1252 "/xyz/openbmc_project/inventory/system/bar_chassis${number}"); 1253 } 1254 1255 // Test where fails: Element is not an array 1256 try 1257 { 1258 const json element = R"( 1259 { 1260 "foo": "bar" 1261 } 1262 )"_json; 1263 parseChassisTemplateArray(element); 1264 ADD_FAILURE() << "Should not have reached this line."; 1265 } 1266 catch (const std::invalid_argument& e) 1267 { 1268 EXPECT_STREQ(e.what(), "Element is not an array"); 1269 } 1270 1271 // Test where fails: Element within array is invalid 1272 try 1273 { 1274 const json element = R"( 1275 [ 1276 { 1277 "id": "foo_chassis", 1278 "number": "${chassis_number}", 1279 "inventory_path": "/xyz/openbmc_project/inventory/system/chassis${chassis_number}", 1280 "power_sequencers": [] 1281 }, 1282 { 1283 "id": "bar_chassis" 1284 } 1285 ] 1286 )"_json; 1287 parseChassisTemplateArray(element); 1288 ADD_FAILURE() << "Should not have reached this line."; 1289 } 1290 catch (const std::invalid_argument& e) 1291 { 1292 EXPECT_STREQ(e.what(), "Required property missing: number"); 1293 } 1294 } 1295 1296 TEST(ConfigFileParserTests, ParseGPIO) 1297 { 1298 // Test where works: Only required properties specified 1299 { 1300 const json element = R"( 1301 { 1302 "line": 60 1303 } 1304 )"_json; 1305 std::map<std::string, std::string> variables{}; 1306 auto gpio = parseGPIO(element, variables); 1307 EXPECT_EQ(gpio.line, 60); 1308 EXPECT_FALSE(gpio.activeLow); 1309 } 1310 1311 // Test where works: All properties specified 1312 { 1313 const json element = R"( 1314 { 1315 "line": 131, 1316 "active_low": true 1317 } 1318 )"_json; 1319 std::map<std::string, std::string> variables{}; 1320 auto gpio = parseGPIO(element, variables); 1321 EXPECT_EQ(gpio.line, 131); 1322 EXPECT_TRUE(gpio.activeLow); 1323 } 1324 1325 // Test where works: Variables specified 1326 { 1327 const json element = R"( 1328 { 1329 "line": "${line}", 1330 "active_low": "${active_low}" 1331 } 1332 )"_json; 1333 std::map<std::string, std::string> variables{{"line", "54"}, 1334 {"active_low", "false"}}; 1335 auto gpio = parseGPIO(element, variables); 1336 EXPECT_EQ(gpio.line, 54); 1337 EXPECT_FALSE(gpio.activeLow); 1338 } 1339 1340 // Test where fails: Element is not an object 1341 try 1342 { 1343 const json element = R"( [ "vdda", "vddb" ] )"_json; 1344 std::map<std::string, std::string> variables{}; 1345 parseGPIO(element, variables); 1346 ADD_FAILURE() << "Should not have reached this line."; 1347 } 1348 catch (const std::invalid_argument& e) 1349 { 1350 EXPECT_STREQ(e.what(), "Element is not an object"); 1351 } 1352 1353 // Test where fails: Required line property not specified 1354 try 1355 { 1356 const json element = R"( 1357 { 1358 "active_low": true 1359 } 1360 )"_json; 1361 std::map<std::string, std::string> variables{}; 1362 parseGPIO(element, variables); 1363 ADD_FAILURE() << "Should not have reached this line."; 1364 } 1365 catch (const std::invalid_argument& e) 1366 { 1367 EXPECT_STREQ(e.what(), "Required property missing: line"); 1368 } 1369 1370 // Test where fails: line value is invalid 1371 try 1372 { 1373 const json element = R"( 1374 { 1375 "line": -131, 1376 "active_low": true 1377 } 1378 )"_json; 1379 std::map<std::string, std::string> variables{}; 1380 parseGPIO(element, variables); 1381 ADD_FAILURE() << "Should not have reached this line."; 1382 } 1383 catch (const std::invalid_argument& e) 1384 { 1385 EXPECT_STREQ(e.what(), "Element is not an unsigned integer"); 1386 } 1387 1388 // Test where fails: active_low value is invalid 1389 try 1390 { 1391 const json element = R"( 1392 { 1393 "line": 131, 1394 "active_low": "true" 1395 } 1396 )"_json; 1397 std::map<std::string, std::string> variables{}; 1398 parseGPIO(element, variables); 1399 ADD_FAILURE() << "Should not have reached this line."; 1400 } 1401 catch (const std::invalid_argument& e) 1402 { 1403 EXPECT_STREQ(e.what(), "Element is not a boolean"); 1404 } 1405 1406 // Test where fails: Invalid property specified 1407 try 1408 { 1409 const json element = R"( 1410 { 1411 "line": 131, 1412 "foo": "bar" 1413 } 1414 )"_json; 1415 std::map<std::string, std::string> variables{}; 1416 parseGPIO(element, variables); 1417 ADD_FAILURE() << "Should not have reached this line."; 1418 } 1419 catch (const std::invalid_argument& e) 1420 { 1421 EXPECT_STREQ(e.what(), "Element contains an invalid property"); 1422 } 1423 1424 // Test where fails: Invalid variable value specified 1425 try 1426 { 1427 const json element = R"( 1428 { 1429 "line": "${line}", 1430 "active_low": "${active_low}" 1431 } 1432 )"_json; 1433 std::map<std::string, std::string> variables{{"line", "-1"}, 1434 {"active_low", "false"}}; 1435 parseGPIO(element, variables); 1436 ADD_FAILURE() << "Should not have reached this line."; 1437 } 1438 catch (const std::invalid_argument& e) 1439 { 1440 EXPECT_STREQ(e.what(), "Element is not an unsigned integer"); 1441 } 1442 } 1443 1444 TEST(ConfigFileParserTests, ParseI2CInterface) 1445 { 1446 // Test where works: No variables 1447 { 1448 const json element = R"( 1449 { 1450 "bus": 2, 1451 "address": "0x70" 1452 } 1453 )"_json; 1454 std::map<std::string, std::string> variables{}; 1455 auto [bus, address] = parseI2CInterface(element, variables); 1456 EXPECT_EQ(bus, 2); 1457 EXPECT_EQ(address, 0x70); 1458 } 1459 1460 // Test where works: Variables specified 1461 { 1462 const json element = R"( 1463 { 1464 "bus": "${bus}", 1465 "address": "${address}" 1466 } 1467 )"_json; 1468 std::map<std::string, std::string> variables{{"bus", "3"}, 1469 {"address", "0x23"}}; 1470 auto [bus, address] = parseI2CInterface(element, variables); 1471 EXPECT_EQ(bus, 3); 1472 EXPECT_EQ(address, 0x23); 1473 } 1474 1475 // Test where fails: Element is not an object 1476 try 1477 { 1478 const json element = R"( [ 1, "0x70" ] )"_json; 1479 std::map<std::string, std::string> variables{}; 1480 parseI2CInterface(element, variables); 1481 ADD_FAILURE() << "Should not have reached this line."; 1482 } 1483 catch (const std::invalid_argument& e) 1484 { 1485 EXPECT_STREQ(e.what(), "Element is not an object"); 1486 } 1487 1488 // Test where fails: Required bus property not specified 1489 try 1490 { 1491 const json element = R"( 1492 { 1493 "address": "0x70" 1494 } 1495 )"_json; 1496 std::map<std::string, std::string> variables{}; 1497 parseI2CInterface(element, variables); 1498 ADD_FAILURE() << "Should not have reached this line."; 1499 } 1500 catch (const std::invalid_argument& e) 1501 { 1502 EXPECT_STREQ(e.what(), "Required property missing: bus"); 1503 } 1504 1505 // Test where fails: Required address property not specified 1506 try 1507 { 1508 const json element = R"( 1509 { 1510 "bus": 2 1511 } 1512 )"_json; 1513 std::map<std::string, std::string> variables{}; 1514 parseI2CInterface(element, variables); 1515 ADD_FAILURE() << "Should not have reached this line."; 1516 } 1517 catch (const std::invalid_argument& e) 1518 { 1519 EXPECT_STREQ(e.what(), "Required property missing: address"); 1520 } 1521 1522 // Test where fails: bus value is invalid 1523 try 1524 { 1525 const json element = R"( 1526 { 1527 "bus": 1.1, 1528 "address": "0x70" 1529 } 1530 )"_json; 1531 std::map<std::string, std::string> variables{}; 1532 parseI2CInterface(element, variables); 1533 ADD_FAILURE() << "Should not have reached this line."; 1534 } 1535 catch (const std::invalid_argument& e) 1536 { 1537 EXPECT_STREQ(e.what(), "Element is not an integer"); 1538 } 1539 1540 // Test where fails: address value is invalid 1541 try 1542 { 1543 const json element = R"( 1544 { 1545 "bus": 2, 1546 "address": 70 1547 } 1548 )"_json; 1549 std::map<std::string, std::string> variables{}; 1550 parseI2CInterface(element, variables); 1551 ADD_FAILURE() << "Should not have reached this line."; 1552 } 1553 catch (const std::invalid_argument& e) 1554 { 1555 EXPECT_STREQ(e.what(), "Element is not a string"); 1556 } 1557 1558 // Test where fails: Invalid property specified 1559 try 1560 { 1561 const json element = R"( 1562 { 1563 "bus": 2, 1564 "address": "0x70", 1565 "foo": "bar" 1566 } 1567 )"_json; 1568 std::map<std::string, std::string> variables{}; 1569 parseI2CInterface(element, variables); 1570 ADD_FAILURE() << "Should not have reached this line."; 1571 } 1572 catch (const std::invalid_argument& e) 1573 { 1574 EXPECT_STREQ(e.what(), "Element contains an invalid property"); 1575 } 1576 1577 // Test where fails: Invalid variable value specified 1578 try 1579 { 1580 const json element = R"( 1581 { 1582 "bus": "${bus}", 1583 "address": "${address}" 1584 } 1585 )"_json; 1586 std::map<std::string, std::string> variables{{"bus", "foo"}, 1587 {"address", "0x23"}}; 1588 parseI2CInterface(element, variables); 1589 ADD_FAILURE() << "Should not have reached this line."; 1590 } 1591 catch (const std::invalid_argument& e) 1592 { 1593 EXPECT_STREQ(e.what(), "Element is not an integer"); 1594 } 1595 } 1596 1597 TEST(ConfigFileParserTests, ParsePowerSequencer) 1598 { 1599 // Test where works: Has comments property: Type is "UCD90160" 1600 { 1601 const json element = R"( 1602 { 1603 "comments": [ "Power sequencer in chassis 1", 1604 "Controls VDD rails" ], 1605 "type": "UCD90160", 1606 "i2c_interface": { "bus": 3, "address": "0x11" }, 1607 "power_control_gpio_name": "power-chassis-control", 1608 "power_good_gpio_name": "power-chassis-good", 1609 "rails": [ { "name": "VDD_CPU0" }, { "name": "VCS_CPU1" } ] 1610 } 1611 )"_json; 1612 std::map<std::string, std::string> variables{}; 1613 MockServices services{}; 1614 auto powerSequencer = parsePowerSequencer(element, variables, services); 1615 EXPECT_EQ(powerSequencer->getName(), "UCD90160"); 1616 EXPECT_EQ(powerSequencer->getBus(), 3); 1617 EXPECT_EQ(powerSequencer->getAddress(), 0x11); 1618 EXPECT_EQ(powerSequencer->getPowerControlGPIOName(), 1619 "power-chassis-control"); 1620 EXPECT_EQ(powerSequencer->getPowerGoodGPIOName(), "power-chassis-good"); 1621 EXPECT_EQ(powerSequencer->getRails().size(), 2); 1622 EXPECT_EQ(powerSequencer->getRails()[0]->getName(), "VDD_CPU0"); 1623 EXPECT_EQ(powerSequencer->getRails()[1]->getName(), "VCS_CPU1"); 1624 } 1625 1626 // Test where works: No comments property: Variables specified: Type is 1627 // "UCD90320" 1628 { 1629 const json element = R"( 1630 { 1631 "type": "${type}", 1632 "i2c_interface": { "bus": "${bus}", "address": "${address}" }, 1633 "power_control_gpio_name": "${power_control_gpio_name}", 1634 "power_good_gpio_name": "${power_good_gpio_name}", 1635 "rails": [ { "name": "${rail1}" }, { "name": "${rail2}" } ] 1636 } 1637 )"_json; 1638 std::map<std::string, std::string> variables{ 1639 {"type", "UCD90320"}, 1640 {"bus", "4"}, 1641 {"address", "0x24"}, 1642 {"power_control_gpio_name", "power_on"}, 1643 {"power_good_gpio_name", "pgood"}, 1644 {"rail1", "cpu1"}, 1645 {"rail2", "cpu2"}}; 1646 MockServices services{}; 1647 auto powerSequencer = parsePowerSequencer(element, variables, services); 1648 EXPECT_EQ(powerSequencer->getName(), "UCD90320"); 1649 EXPECT_EQ(powerSequencer->getBus(), 4); 1650 EXPECT_EQ(powerSequencer->getAddress(), 0x24); 1651 EXPECT_EQ(powerSequencer->getPowerControlGPIOName(), "power_on"); 1652 EXPECT_EQ(powerSequencer->getPowerGoodGPIOName(), "pgood"); 1653 EXPECT_EQ(powerSequencer->getRails().size(), 2); 1654 EXPECT_EQ(powerSequencer->getRails()[0]->getName(), "cpu1"); 1655 EXPECT_EQ(powerSequencer->getRails()[1]->getName(), "cpu2"); 1656 } 1657 1658 // Test where fails: Element is not an object 1659 try 1660 { 1661 const json element = R"( [ "vdda", "vddb" ] )"_json; 1662 std::map<std::string, std::string> variables{}; 1663 MockServices services{}; 1664 parsePowerSequencer(element, variables, services); 1665 ADD_FAILURE() << "Should not have reached this line."; 1666 } 1667 catch (const std::invalid_argument& e) 1668 { 1669 EXPECT_STREQ(e.what(), "Element is not an object"); 1670 } 1671 1672 // Test where fails: Required type property not specified 1673 try 1674 { 1675 const json element = R"( 1676 { 1677 "i2c_interface": { "bus": 3, "address": "0x11" }, 1678 "power_control_gpio_name": "power-chassis-control", 1679 "power_good_gpio_name": "power-chassis-good", 1680 "rails": [] 1681 } 1682 )"_json; 1683 std::map<std::string, std::string> variables{}; 1684 MockServices services{}; 1685 parsePowerSequencer(element, variables, services); 1686 ADD_FAILURE() << "Should not have reached this line."; 1687 } 1688 catch (const std::invalid_argument& e) 1689 { 1690 EXPECT_STREQ(e.what(), "Required property missing: type"); 1691 } 1692 1693 // Test where fails: Required i2c_interface property not specified 1694 try 1695 { 1696 const json element = R"( 1697 { 1698 "type": "UCD90320", 1699 "power_control_gpio_name": "power-chassis-control", 1700 "power_good_gpio_name": "power-chassis-good", 1701 "rails": [] 1702 } 1703 )"_json; 1704 std::map<std::string, std::string> variables{}; 1705 MockServices services{}; 1706 parsePowerSequencer(element, variables, services); 1707 ADD_FAILURE() << "Should not have reached this line."; 1708 } 1709 catch (const std::invalid_argument& e) 1710 { 1711 EXPECT_STREQ(e.what(), "Required property missing: i2c_interface"); 1712 } 1713 1714 // Test where fails: Required power_control_gpio_name property not specified 1715 try 1716 { 1717 const json element = R"( 1718 { 1719 "type": "UCD90320", 1720 "i2c_interface": { "bus": 3, "address": "0x11" }, 1721 "power_good_gpio_name": "power-chassis-good", 1722 "rails": [] 1723 } 1724 )"_json; 1725 std::map<std::string, std::string> variables{}; 1726 MockServices services{}; 1727 parsePowerSequencer(element, variables, services); 1728 ADD_FAILURE() << "Should not have reached this line."; 1729 } 1730 catch (const std::invalid_argument& e) 1731 { 1732 EXPECT_STREQ(e.what(), 1733 "Required property missing: power_control_gpio_name"); 1734 } 1735 1736 // Test where fails: Required power_good_gpio_name property not specified 1737 try 1738 { 1739 const json element = R"( 1740 { 1741 "type": "UCD90320", 1742 "i2c_interface": { "bus": 3, "address": "0x11" }, 1743 "power_control_gpio_name": "power-chassis-control", 1744 "rails": [] 1745 } 1746 )"_json; 1747 std::map<std::string, std::string> variables{}; 1748 MockServices services{}; 1749 parsePowerSequencer(element, variables, services); 1750 ADD_FAILURE() << "Should not have reached this line."; 1751 } 1752 catch (const std::invalid_argument& e) 1753 { 1754 EXPECT_STREQ(e.what(), 1755 "Required property missing: power_good_gpio_name"); 1756 } 1757 1758 // Test where fails: Required rails property not specified 1759 try 1760 { 1761 const json element = R"( 1762 { 1763 "type": "UCD90320", 1764 "i2c_interface": { "bus": 3, "address": "0x11" }, 1765 "power_control_gpio_name": "power-chassis-control", 1766 "power_good_gpio_name": "power-chassis-good" 1767 } 1768 )"_json; 1769 std::map<std::string, std::string> variables{}; 1770 MockServices services{}; 1771 parsePowerSequencer(element, variables, services); 1772 ADD_FAILURE() << "Should not have reached this line."; 1773 } 1774 catch (const std::invalid_argument& e) 1775 { 1776 EXPECT_STREQ(e.what(), "Required property missing: rails"); 1777 } 1778 1779 // Test where fails: type value is invalid: Not a string 1780 try 1781 { 1782 const json element = R"( 1783 { 1784 "type": true, 1785 "i2c_interface": { "bus": 3, "address": "0x11" }, 1786 "power_control_gpio_name": "power-chassis-control", 1787 "power_good_gpio_name": "power-chassis-good", 1788 "rails": [] 1789 } 1790 )"_json; 1791 std::map<std::string, std::string> variables{}; 1792 MockServices services{}; 1793 parsePowerSequencer(element, variables, services); 1794 ADD_FAILURE() << "Should not have reached this line."; 1795 } 1796 catch (const std::invalid_argument& e) 1797 { 1798 EXPECT_STREQ(e.what(), "Element is not a string"); 1799 } 1800 1801 // Test where fails: type value is invalid: Not a supported type 1802 try 1803 { 1804 const json element = R"( 1805 { 1806 "type": "foo_bar", 1807 "i2c_interface": { "bus": 3, "address": "0x11" }, 1808 "power_control_gpio_name": "power-chassis-control", 1809 "power_good_gpio_name": "power-chassis-good", 1810 "rails": [] 1811 } 1812 )"_json; 1813 std::map<std::string, std::string> variables{}; 1814 MockServices services{}; 1815 parsePowerSequencer(element, variables, services); 1816 ADD_FAILURE() << "Should not have reached this line."; 1817 } 1818 catch (const std::invalid_argument& e) 1819 { 1820 EXPECT_STREQ(e.what(), "Invalid power sequencer type: foo_bar"); 1821 } 1822 1823 // Test where fails: i2c_interface value is invalid 1824 try 1825 { 1826 const json element = R"( 1827 { 1828 "type": "UCD90320", 1829 "i2c_interface": 3, 1830 "power_control_gpio_name": "power-chassis-control", 1831 "power_good_gpio_name": "power-chassis-good", 1832 "rails": [] 1833 } 1834 )"_json; 1835 std::map<std::string, std::string> variables{}; 1836 MockServices services{}; 1837 parsePowerSequencer(element, variables, services); 1838 ADD_FAILURE() << "Should not have reached this line."; 1839 } 1840 catch (const std::invalid_argument& e) 1841 { 1842 EXPECT_STREQ(e.what(), "Element is not an object"); 1843 } 1844 1845 // Test where fails: power_control_gpio_name value is invalid 1846 try 1847 { 1848 const json element = R"( 1849 { 1850 "type": "UCD90320", 1851 "i2c_interface": { "bus": 3, "address": "0x11" }, 1852 "power_control_gpio_name": [], 1853 "power_good_gpio_name": "power-chassis-good", 1854 "rails": [] 1855 } 1856 )"_json; 1857 std::map<std::string, std::string> variables{}; 1858 MockServices services{}; 1859 parsePowerSequencer(element, variables, services); 1860 ADD_FAILURE() << "Should not have reached this line."; 1861 } 1862 catch (const std::invalid_argument& e) 1863 { 1864 EXPECT_STREQ(e.what(), "Element is not a string"); 1865 } 1866 1867 // Test where fails: power_good_gpio_name value is invalid 1868 try 1869 { 1870 const json element = R"( 1871 { 1872 "type": "UCD90320", 1873 "i2c_interface": { "bus": 3, "address": "0x11" }, 1874 "power_control_gpio_name": "power-chassis-control", 1875 "power_good_gpio_name": 12, 1876 "rails": [] 1877 } 1878 )"_json; 1879 std::map<std::string, std::string> variables{}; 1880 MockServices services{}; 1881 parsePowerSequencer(element, variables, services); 1882 ADD_FAILURE() << "Should not have reached this line."; 1883 } 1884 catch (const std::invalid_argument& e) 1885 { 1886 EXPECT_STREQ(e.what(), "Element is not a string"); 1887 } 1888 1889 // Test where fails: rails value is invalid 1890 try 1891 { 1892 const json element = R"( 1893 { 1894 "type": "UCD90320", 1895 "i2c_interface": { "bus": 3, "address": "0x11" }, 1896 "power_control_gpio_name": "power-chassis-control", 1897 "power_good_gpio_name": "power-chassis-good", 1898 "rails": [ { "name": 33 } ] 1899 } 1900 )"_json; 1901 std::map<std::string, std::string> variables{}; 1902 MockServices services{}; 1903 parsePowerSequencer(element, variables, services); 1904 ADD_FAILURE() << "Should not have reached this line."; 1905 } 1906 catch (const std::invalid_argument& e) 1907 { 1908 EXPECT_STREQ(e.what(), "Element is not a string"); 1909 } 1910 1911 // Test where fails: Invalid property specified 1912 try 1913 { 1914 const json element = R"( 1915 { 1916 "type": "UCD90320", 1917 "i2c_interface": { "bus": 3, "address": "0x11" }, 1918 "power_control_gpio_name": "power-chassis-control", 1919 "power_good_gpio_name": "power-chassis-good", 1920 "driver_name": "foo", 1921 "rails": [] 1922 } 1923 )"_json; 1924 std::map<std::string, std::string> variables{}; 1925 MockServices services{}; 1926 parsePowerSequencer(element, variables, services); 1927 ADD_FAILURE() << "Should not have reached this line."; 1928 } 1929 catch (const std::invalid_argument& e) 1930 { 1931 EXPECT_STREQ(e.what(), "Element contains an invalid property"); 1932 } 1933 1934 // Test where fails: Invalid variable value specified 1935 try 1936 { 1937 const json element = R"( 1938 { 1939 "type": "UCD90320", 1940 "i2c_interface": { "bus": "${bus}", "address": "0x11" }, 1941 "power_control_gpio_name": "power-chassis-control", 1942 "power_good_gpio_name": "power-chassis-good", 1943 "rails": [] 1944 } 1945 )"_json; 1946 std::map<std::string, std::string> variables{{"bus", "two"}}; 1947 MockServices services{}; 1948 parsePowerSequencer(element, variables, services); 1949 ADD_FAILURE() << "Should not have reached this line."; 1950 } 1951 catch (const std::invalid_argument& e) 1952 { 1953 EXPECT_STREQ(e.what(), "Element is not an integer"); 1954 } 1955 } 1956 1957 TEST(ConfigFileParserTests, ParsePowerSequencerArray) 1958 { 1959 // Test where works: Array is empty 1960 { 1961 const json element = R"( 1962 [ 1963 ] 1964 )"_json; 1965 std::map<std::string, std::string> variables{}; 1966 MockServices services{}; 1967 auto powerSequencers = 1968 parsePowerSequencerArray(element, variables, services); 1969 EXPECT_EQ(powerSequencers.size(), 0); 1970 } 1971 1972 // Test where works: Array is not empty 1973 { 1974 const json element = R"( 1975 [ 1976 { 1977 "type": "UCD90160", 1978 "i2c_interface": { "bus": 3, "address": "0x11" }, 1979 "power_control_gpio_name": "power-chassis-control1", 1980 "power_good_gpio_name": "power-chassis-good1", 1981 "rails": [] 1982 }, 1983 { 1984 "type": "UCD90320", 1985 "i2c_interface": { "bus": 4, "address": "0x70" }, 1986 "power_control_gpio_name": "power-chassis-control2", 1987 "power_good_gpio_name": "power-chassis-good2", 1988 "rails": [] 1989 } 1990 ] 1991 )"_json; 1992 std::map<std::string, std::string> variables{}; 1993 MockServices services{}; 1994 auto powerSequencers = 1995 parsePowerSequencerArray(element, variables, services); 1996 EXPECT_EQ(powerSequencers.size(), 2); 1997 EXPECT_EQ(powerSequencers[0]->getName(), "UCD90160"); 1998 EXPECT_EQ(powerSequencers[0]->getBus(), 3); 1999 EXPECT_EQ(powerSequencers[0]->getAddress(), 0x11); 2000 EXPECT_EQ(powerSequencers[1]->getName(), "UCD90320"); 2001 EXPECT_EQ(powerSequencers[1]->getBus(), 4); 2002 EXPECT_EQ(powerSequencers[1]->getAddress(), 0x70); 2003 } 2004 2005 // Test where works: Variables specified 2006 { 2007 const json element = R"( 2008 [ 2009 { 2010 "type": "UCD90160", 2011 "i2c_interface": { "bus": "${bus1}", "address": "${address1}" }, 2012 "power_control_gpio_name": "power-chassis-control1", 2013 "power_good_gpio_name": "power-chassis-good1", 2014 "rails": [] 2015 }, 2016 { 2017 "type": "UCD90320", 2018 "i2c_interface": { "bus": "${bus2}", "address": "${address2}" }, 2019 "power_control_gpio_name": "power-chassis-control2", 2020 "power_good_gpio_name": "power-chassis-good2", 2021 "rails": [] 2022 } 2023 ] 2024 )"_json; 2025 std::map<std::string, std::string> variables{ 2026 {"bus1", "5"}, 2027 {"address1", "0x22"}, 2028 {"bus2", "7"}, 2029 {"address2", "0x49"}}; 2030 MockServices services{}; 2031 auto powerSequencers = 2032 parsePowerSequencerArray(element, variables, services); 2033 EXPECT_EQ(powerSequencers.size(), 2); 2034 EXPECT_EQ(powerSequencers[0]->getName(), "UCD90160"); 2035 EXPECT_EQ(powerSequencers[0]->getBus(), 5); 2036 EXPECT_EQ(powerSequencers[0]->getAddress(), 0x22); 2037 EXPECT_EQ(powerSequencers[1]->getName(), "UCD90320"); 2038 EXPECT_EQ(powerSequencers[1]->getBus(), 7); 2039 EXPECT_EQ(powerSequencers[1]->getAddress(), 0x49); 2040 } 2041 2042 // Test where fails: Element is not an array 2043 try 2044 { 2045 const json element = R"( 2046 { 2047 "foo": "bar" 2048 } 2049 )"_json; 2050 std::map<std::string, std::string> variables{}; 2051 MockServices services{}; 2052 parsePowerSequencerArray(element, variables, services); 2053 ADD_FAILURE() << "Should not have reached this line."; 2054 } 2055 catch (const std::invalid_argument& e) 2056 { 2057 EXPECT_STREQ(e.what(), "Element is not an array"); 2058 } 2059 2060 // Test where fails: Element within array is invalid 2061 try 2062 { 2063 const json element = R"( 2064 [ 2065 { 2066 "type": "UCD90160", 2067 "i2c_interface": { "bus": 3, "address": "0x11" }, 2068 "power_control_gpio_name": "power-chassis-control1", 2069 "power_good_gpio_name": "power-chassis-good1", 2070 "rails": [] 2071 }, 2072 true 2073 ] 2074 )"_json; 2075 std::map<std::string, std::string> variables{}; 2076 MockServices services{}; 2077 parsePowerSequencerArray(element, variables, services); 2078 ADD_FAILURE() << "Should not have reached this line."; 2079 } 2080 catch (const std::invalid_argument& e) 2081 { 2082 EXPECT_STREQ(e.what(), "Element is not an object"); 2083 } 2084 2085 // Test where fails: Invalid variable value specified 2086 try 2087 { 2088 const json element = R"( 2089 [ 2090 { 2091 "type": "UCD90320", 2092 "i2c_interface": { "bus": "${bus}", "address": "${address}" }, 2093 "power_control_gpio_name": "power-chassis-control", 2094 "power_good_gpio_name": "power-chassis-good", 2095 "rails": [] 2096 } 2097 ] 2098 )"_json; 2099 std::map<std::string, std::string> variables{{"bus", "7"}, 2100 {"address", "70"}}; 2101 MockServices services{}; 2102 parsePowerSequencerArray(element, variables, services); 2103 ADD_FAILURE() << "Should not have reached this line."; 2104 } 2105 catch (const std::invalid_argument& e) 2106 { 2107 EXPECT_STREQ(e.what(), "Element is not hexadecimal string"); 2108 } 2109 } 2110 2111 TEST(ConfigFileParserTests, ParseRail) 2112 { 2113 // Test where works: Only required properties specified 2114 { 2115 const json element = R"( 2116 { 2117 "name": "VDD_CPU0" 2118 } 2119 )"_json; 2120 std::map<std::string, std::string> variables{}; 2121 auto rail = parseRail(element, variables); 2122 EXPECT_EQ(rail->getName(), "VDD_CPU0"); 2123 EXPECT_FALSE(rail->getPresence().has_value()); 2124 EXPECT_FALSE(rail->getPage().has_value()); 2125 EXPECT_FALSE(rail->isPowerSupplyRail()); 2126 EXPECT_FALSE(rail->getCheckStatusVout()); 2127 EXPECT_FALSE(rail->getCompareVoltageToLimit()); 2128 EXPECT_FALSE(rail->getGPIO().has_value()); 2129 } 2130 2131 // Test where works: All properties specified 2132 { 2133 const json element = R"( 2134 { 2135 "name": "12.0VB", 2136 "presence": "/xyz/openbmc_project/inventory/system/chassis/powersupply1", 2137 "page": 11, 2138 "is_power_supply_rail": true, 2139 "check_status_vout": true, 2140 "compare_voltage_to_limit": true, 2141 "gpio": { "line": 60, "active_low": true } 2142 } 2143 )"_json; 2144 std::map<std::string, std::string> variables{}; 2145 auto rail = parseRail(element, variables); 2146 EXPECT_EQ(rail->getName(), "12.0VB"); 2147 EXPECT_TRUE(rail->getPresence().has_value()); 2148 EXPECT_EQ(rail->getPresence().value(), 2149 "/xyz/openbmc_project/inventory/system/chassis/powersupply1"); 2150 EXPECT_TRUE(rail->getPage().has_value()); 2151 EXPECT_EQ(rail->getPage().value(), 11); 2152 EXPECT_TRUE(rail->isPowerSupplyRail()); 2153 EXPECT_TRUE(rail->getCheckStatusVout()); 2154 EXPECT_TRUE(rail->getCompareVoltageToLimit()); 2155 EXPECT_TRUE(rail->getGPIO().has_value()); 2156 EXPECT_EQ(rail->getGPIO().value().line, 60); 2157 EXPECT_TRUE(rail->getGPIO().value().activeLow); 2158 } 2159 2160 // Test where works: Variables specified 2161 { 2162 const json element = R"( 2163 { 2164 "name": "${name}", 2165 "presence": "${presence}", 2166 "page": "${page}", 2167 "is_power_supply_rail": "${is_power_supply_rail}", 2168 "check_status_vout": "${check_status_vout}", 2169 "compare_voltage_to_limit": "${compare_voltage_to_limit}", 2170 "gpio": { "line": "${line}", "active_low": true } 2171 } 2172 )"_json; 2173 std::map<std::string, std::string> variables{ 2174 {"name", "vdd"}, 2175 {"presence", 2176 "/xyz/openbmc_project/inventory/system/chassis/powersupply2"}, 2177 {"page", "9"}, 2178 {"is_power_supply_rail", "true"}, 2179 {"check_status_vout", "false"}, 2180 {"compare_voltage_to_limit", "true"}, 2181 {"line", "72"}}; 2182 auto rail = parseRail(element, variables); 2183 EXPECT_EQ(rail->getName(), "vdd"); 2184 EXPECT_TRUE(rail->getPresence().has_value()); 2185 EXPECT_EQ(rail->getPresence().value(), 2186 "/xyz/openbmc_project/inventory/system/chassis/powersupply2"); 2187 EXPECT_TRUE(rail->getPage().has_value()); 2188 EXPECT_EQ(rail->getPage().value(), 9); 2189 EXPECT_TRUE(rail->isPowerSupplyRail()); 2190 EXPECT_FALSE(rail->getCheckStatusVout()); 2191 EXPECT_TRUE(rail->getCompareVoltageToLimit()); 2192 EXPECT_TRUE(rail->getGPIO().has_value()); 2193 EXPECT_EQ(rail->getGPIO().value().line, 72); 2194 EXPECT_TRUE(rail->getGPIO().value().activeLow); 2195 } 2196 2197 // Test where fails: Element is not an object 2198 try 2199 { 2200 const json element = R"( [ "vdda", "vddb" ] )"_json; 2201 std::map<std::string, std::string> variables{}; 2202 parseRail(element, variables); 2203 ADD_FAILURE() << "Should not have reached this line."; 2204 } 2205 catch (const std::invalid_argument& e) 2206 { 2207 EXPECT_STREQ(e.what(), "Element is not an object"); 2208 } 2209 2210 // Test where fails: Required name property not specified 2211 try 2212 { 2213 const json element = R"( 2214 { 2215 "page": 11 2216 } 2217 )"_json; 2218 std::map<std::string, std::string> variables{}; 2219 parseRail(element, variables); 2220 ADD_FAILURE() << "Should not have reached this line."; 2221 } 2222 catch (const std::invalid_argument& e) 2223 { 2224 EXPECT_STREQ(e.what(), "Required property missing: name"); 2225 } 2226 2227 // Test where fails: name value is invalid 2228 try 2229 { 2230 const json element = R"( 2231 { 2232 "name": 31, 2233 "page": 11 2234 } 2235 )"_json; 2236 std::map<std::string, std::string> variables{}; 2237 parseRail(element, variables); 2238 ADD_FAILURE() << "Should not have reached this line."; 2239 } 2240 catch (const std::invalid_argument& e) 2241 { 2242 EXPECT_STREQ(e.what(), "Element is not a string"); 2243 } 2244 2245 // Test where fails: presence value is invalid 2246 try 2247 { 2248 const json element = R"( 2249 { 2250 "name": "VCS_CPU1", 2251 "presence": false 2252 } 2253 )"_json; 2254 std::map<std::string, std::string> variables{}; 2255 parseRail(element, variables); 2256 ADD_FAILURE() << "Should not have reached this line."; 2257 } 2258 catch (const std::invalid_argument& e) 2259 { 2260 EXPECT_STREQ(e.what(), "Element is not a string"); 2261 } 2262 2263 // Test where fails: page value is invalid 2264 try 2265 { 2266 const json element = R"( 2267 { 2268 "name": "VCS_CPU1", 2269 "page": 256 2270 } 2271 )"_json; 2272 std::map<std::string, std::string> variables{}; 2273 parseRail(element, variables); 2274 ADD_FAILURE() << "Should not have reached this line."; 2275 } 2276 catch (const std::invalid_argument& e) 2277 { 2278 EXPECT_STREQ(e.what(), "Element is not an 8-bit unsigned integer"); 2279 } 2280 2281 // Test where fails: is_power_supply_rail value is invalid 2282 try 2283 { 2284 const json element = R"( 2285 { 2286 "name": "12.0VA", 2287 "is_power_supply_rail": "true" 2288 } 2289 )"_json; 2290 std::map<std::string, std::string> variables{}; 2291 parseRail(element, variables); 2292 ADD_FAILURE() << "Should not have reached this line."; 2293 } 2294 catch (const std::invalid_argument& e) 2295 { 2296 EXPECT_STREQ(e.what(), "Element is not a boolean"); 2297 } 2298 2299 // Test where fails: check_status_vout value is invalid 2300 try 2301 { 2302 const json element = R"( 2303 { 2304 "name": "VCS_CPU1", 2305 "check_status_vout": "false" 2306 } 2307 )"_json; 2308 std::map<std::string, std::string> variables{}; 2309 parseRail(element, variables); 2310 ADD_FAILURE() << "Should not have reached this line."; 2311 } 2312 catch (const std::invalid_argument& e) 2313 { 2314 EXPECT_STREQ(e.what(), "Element is not a boolean"); 2315 } 2316 2317 // Test where fails: compare_voltage_to_limit value is invalid 2318 try 2319 { 2320 const json element = R"( 2321 { 2322 "name": "VCS_CPU1", 2323 "compare_voltage_to_limit": 23 2324 } 2325 )"_json; 2326 std::map<std::string, std::string> variables{}; 2327 parseRail(element, variables); 2328 ADD_FAILURE() << "Should not have reached this line."; 2329 } 2330 catch (const std::invalid_argument& e) 2331 { 2332 EXPECT_STREQ(e.what(), "Element is not a boolean"); 2333 } 2334 2335 // Test where fails: gpio value is invalid 2336 try 2337 { 2338 const json element = R"( 2339 { 2340 "name": "VCS_CPU1", 2341 "gpio": 131 2342 } 2343 )"_json; 2344 std::map<std::string, std::string> variables{}; 2345 parseRail(element, variables); 2346 ADD_FAILURE() << "Should not have reached this line."; 2347 } 2348 catch (const std::invalid_argument& e) 2349 { 2350 EXPECT_STREQ(e.what(), "Element is not an object"); 2351 } 2352 2353 // Test where fails: check_status_vout is true and page not specified 2354 try 2355 { 2356 const json element = R"( 2357 { 2358 "name": "VCS_CPU1", 2359 "check_status_vout": true 2360 } 2361 )"_json; 2362 std::map<std::string, std::string> variables{}; 2363 parseRail(element, variables); 2364 ADD_FAILURE() << "Should not have reached this line."; 2365 } 2366 catch (const std::invalid_argument& e) 2367 { 2368 EXPECT_STREQ(e.what(), "Required property missing: page"); 2369 } 2370 2371 // Test where fails: compare_voltage_to_limit is true and page not 2372 // specified 2373 try 2374 { 2375 const json element = R"( 2376 { 2377 "name": "VCS_CPU1", 2378 "compare_voltage_to_limit": true 2379 } 2380 )"_json; 2381 std::map<std::string, std::string> variables{}; 2382 parseRail(element, variables); 2383 ADD_FAILURE() << "Should not have reached this line."; 2384 } 2385 catch (const std::invalid_argument& e) 2386 { 2387 EXPECT_STREQ(e.what(), "Required property missing: page"); 2388 } 2389 2390 // Test where fails: Invalid property specified 2391 try 2392 { 2393 const json element = R"( 2394 { 2395 "name": "VCS_CPU1", 2396 "foo": "bar" 2397 } 2398 )"_json; 2399 std::map<std::string, std::string> variables{}; 2400 parseRail(element, variables); 2401 ADD_FAILURE() << "Should not have reached this line."; 2402 } 2403 catch (const std::invalid_argument& e) 2404 { 2405 EXPECT_STREQ(e.what(), "Element contains an invalid property"); 2406 } 2407 2408 // Test where fails: Undefined variable specified 2409 try 2410 { 2411 const json element = R"( 2412 { 2413 "name": "12.0VB", 2414 "presence": "/xyz/openbmc_project/inventory/system/chassis/powersupply${chassis}" 2415 } 2416 )"_json; 2417 std::map<std::string, std::string> variables{{"foo", "bar"}}; 2418 parseRail(element, variables); 2419 ADD_FAILURE() << "Should not have reached this line."; 2420 } 2421 catch (const std::invalid_argument& e) 2422 { 2423 EXPECT_STREQ(e.what(), "Undefined variable: chassis"); 2424 } 2425 } 2426 2427 TEST(ConfigFileParserTests, ParseRailArray) 2428 { 2429 // Test where works: Array is empty 2430 { 2431 const json element = R"( 2432 [ 2433 ] 2434 )"_json; 2435 std::map<std::string, std::string> variables{}; 2436 auto rails = parseRailArray(element, variables); 2437 EXPECT_EQ(rails.size(), 0); 2438 } 2439 2440 // Test where works: Array is not empty 2441 { 2442 const json element = R"( 2443 [ 2444 { "name": "VDD_CPU0" }, 2445 { "name": "VCS_CPU1" } 2446 ] 2447 )"_json; 2448 std::map<std::string, std::string> variables{}; 2449 auto rails = parseRailArray(element, variables); 2450 EXPECT_EQ(rails.size(), 2); 2451 EXPECT_EQ(rails[0]->getName(), "VDD_CPU0"); 2452 EXPECT_EQ(rails[1]->getName(), "VCS_CPU1"); 2453 } 2454 2455 // Test where works: Variables specified 2456 { 2457 const json element = R"( 2458 [ 2459 { "name": "${rail1}" }, 2460 { "name": "${rail2}" }, 2461 { "name": "${rail3}" } 2462 ] 2463 )"_json; 2464 std::map<std::string, std::string> variables{ 2465 {"rail1", "foo"}, {"rail2", "bar"}, {"rail3", "baz"}}; 2466 auto rails = parseRailArray(element, variables); 2467 EXPECT_EQ(rails.size(), 3); 2468 EXPECT_EQ(rails[0]->getName(), "foo"); 2469 EXPECT_EQ(rails[1]->getName(), "bar"); 2470 EXPECT_EQ(rails[2]->getName(), "baz"); 2471 } 2472 2473 // Test where fails: Element is not an array 2474 try 2475 { 2476 const json element = R"( 2477 { 2478 "foo": "bar" 2479 } 2480 )"_json; 2481 std::map<std::string, std::string> variables{}; 2482 parseRailArray(element, variables); 2483 ADD_FAILURE() << "Should not have reached this line."; 2484 } 2485 catch (const std::invalid_argument& e) 2486 { 2487 EXPECT_STREQ(e.what(), "Element is not an array"); 2488 } 2489 2490 // Test where fails: Element within array is invalid 2491 try 2492 { 2493 const json element = R"( 2494 [ 2495 { "name": "VDD_CPU0" }, 2496 23 2497 ] 2498 )"_json; 2499 std::map<std::string, std::string> variables{}; 2500 parseRailArray(element, variables); 2501 ADD_FAILURE() << "Should not have reached this line."; 2502 } 2503 catch (const std::invalid_argument& e) 2504 { 2505 EXPECT_STREQ(e.what(), "Element is not an object"); 2506 } 2507 2508 // Test where fails: Invalid variable value specified 2509 try 2510 { 2511 const json element = R"( 2512 [ 2513 { "name": "VDD_CPU0", "page": "${page1}" }, 2514 { "name": "VCS_CPU1", "page": "${page2}" } 2515 ] 2516 )"_json; 2517 std::map<std::string, std::string> variables{{"page1", "11"}, 2518 {"page2", "-1"}}; 2519 parseRailArray(element, variables); 2520 ADD_FAILURE() << "Should not have reached this line."; 2521 } 2522 catch (const std::invalid_argument& e) 2523 { 2524 EXPECT_STREQ(e.what(), "Element is not an 8-bit unsigned integer"); 2525 } 2526 } 2527 2528 TEST(ConfigFileParserTests, ParseRoot) 2529 { 2530 // Test where works: Only required properties specified 2531 { 2532 const json element = R"( 2533 { 2534 "chassis": [ 2535 { 2536 "number": 1, 2537 "inventory_path": "/xyz/openbmc_project/inventory/system/chassis1", 2538 "power_sequencers": [] 2539 }, 2540 { 2541 "number": 2, 2542 "inventory_path": "/xyz/openbmc_project/inventory/system/chassis2", 2543 "power_sequencers": [] 2544 } 2545 ] 2546 } 2547 )"_json; 2548 MockServices services{}; 2549 auto chassis = parseRoot(element, services); 2550 EXPECT_EQ(chassis.size(), 2); 2551 EXPECT_EQ(chassis[0]->getNumber(), 1); 2552 EXPECT_EQ(chassis[0]->getInventoryPath(), 2553 "/xyz/openbmc_project/inventory/system/chassis1"); 2554 EXPECT_EQ(chassis[1]->getNumber(), 2); 2555 EXPECT_EQ(chassis[1]->getInventoryPath(), 2556 "/xyz/openbmc_project/inventory/system/chassis2"); 2557 } 2558 2559 // Test where works: All properties specified 2560 { 2561 const json element = R"( 2562 { 2563 "comments": [ "Config file for a FooBar one-chassis system" ], 2564 "chassis_templates": [ 2565 { 2566 "id": "foo_chassis", 2567 "number": "${chassis_number}", 2568 "inventory_path": "/xyz/openbmc_project/inventory/system/chassis${chassis_number}", 2569 "power_sequencers": [] 2570 }, 2571 { 2572 "id": "bar_chassis", 2573 "number": "${chassis_number}", 2574 "inventory_path": "/xyz/openbmc_project/inventory/system/bar_chassis${chassis_number}", 2575 "power_sequencers": [] 2576 } 2577 ], 2578 "chassis": [ 2579 { 2580 "template_id": "foo_chassis", 2581 "template_variable_values": { "chassis_number": "2" } 2582 }, 2583 { 2584 "template_id": "bar_chassis", 2585 "template_variable_values": { "chassis_number": "3" } 2586 } 2587 ] 2588 } 2589 )"_json; 2590 MockServices services{}; 2591 auto chassis = parseRoot(element, services); 2592 EXPECT_EQ(chassis.size(), 2); 2593 EXPECT_EQ(chassis[0]->getNumber(), 2); 2594 EXPECT_EQ(chassis[0]->getInventoryPath(), 2595 "/xyz/openbmc_project/inventory/system/chassis2"); 2596 EXPECT_EQ(chassis[1]->getNumber(), 3); 2597 EXPECT_EQ(chassis[1]->getInventoryPath(), 2598 "/xyz/openbmc_project/inventory/system/bar_chassis3"); 2599 } 2600 2601 // Test where fails: Element is not an object 2602 try 2603 { 2604 const json element = R"( [ "VDD_CPU0", "VCS_CPU1" ] )"_json; 2605 MockServices services{}; 2606 parseRoot(element, services); 2607 ADD_FAILURE() << "Should not have reached this line."; 2608 } 2609 catch (const std::invalid_argument& e) 2610 { 2611 EXPECT_STREQ(e.what(), "Element is not an object"); 2612 } 2613 2614 // Test where fails: Required chassis property not specified 2615 try 2616 { 2617 const json element = R"( 2618 { 2619 "comments": [ "Config file for a FooBar one-chassis system" ], 2620 "chassis_templates": [ 2621 { 2622 "id": "foo_chassis", 2623 "number": "${chassis_number}", 2624 "inventory_path": "/xyz/openbmc_project/inventory/system/chassis${chassis_number}", 2625 "power_sequencers": [] 2626 } 2627 ] 2628 } 2629 )"_json; 2630 MockServices services{}; 2631 parseRoot(element, services); 2632 ADD_FAILURE() << "Should not have reached this line."; 2633 } 2634 catch (const std::invalid_argument& e) 2635 { 2636 EXPECT_STREQ(e.what(), "Required property missing: chassis"); 2637 } 2638 2639 // Test where fails: chassis_templates value is invalid 2640 try 2641 { 2642 const json element = R"( 2643 { 2644 "chassis_templates": true, 2645 "chassis": [ 2646 { 2647 "number": 1, 2648 "inventory_path": "/xyz/openbmc_project/inventory/system/chassis1", 2649 "power_sequencers": [] 2650 } 2651 ] 2652 } 2653 )"_json; 2654 MockServices services{}; 2655 parseRoot(element, services); 2656 ADD_FAILURE() << "Should not have reached this line."; 2657 } 2658 catch (const std::invalid_argument& e) 2659 { 2660 EXPECT_STREQ(e.what(), "Element is not an array"); 2661 } 2662 2663 // Test where fails: chassis value is invalid 2664 try 2665 { 2666 const json element = R"( 2667 { 2668 "chassis": [ 2669 { 2670 "number": "one", 2671 "inventory_path": "/xyz/openbmc_project/inventory/system/chassis1", 2672 "power_sequencers": [] 2673 } 2674 ] 2675 } 2676 )"_json; 2677 MockServices services{}; 2678 parseRoot(element, services); 2679 ADD_FAILURE() << "Should not have reached this line."; 2680 } 2681 catch (const std::invalid_argument& e) 2682 { 2683 EXPECT_STREQ(e.what(), "Element is not an integer"); 2684 } 2685 2686 // Test where fails: Invalid property specified 2687 try 2688 { 2689 const json element = R"( 2690 { 2691 "chassis": [ 2692 { 2693 "number": 1, 2694 "inventory_path": "/xyz/openbmc_project/inventory/system/chassis1", 2695 "power_sequencers": [] 2696 } 2697 ], 2698 "foo": true 2699 } 2700 )"_json; 2701 MockServices services{}; 2702 parseRoot(element, services); 2703 ADD_FAILURE() << "Should not have reached this line."; 2704 } 2705 catch (const std::invalid_argument& e) 2706 { 2707 EXPECT_STREQ(e.what(), "Element contains an invalid property"); 2708 } 2709 } 2710 2711 TEST(ConfigFileParserTests, ParseVariables) 2712 { 2713 // Test where works: No variables specified 2714 { 2715 const json element = R"( 2716 { 2717 } 2718 )"_json; 2719 auto variables = parseVariables(element); 2720 EXPECT_EQ(variables.size(), 0); 2721 } 2722 2723 // Test where works: Variables specified 2724 { 2725 const json element = R"( 2726 { 2727 "chassis_number": "2", 2728 "bus_number": "13" 2729 } 2730 )"_json; 2731 auto variables = parseVariables(element); 2732 EXPECT_EQ(variables.size(), 2); 2733 EXPECT_EQ(variables["chassis_number"], "2"); 2734 EXPECT_EQ(variables["bus_number"], "13"); 2735 } 2736 2737 // Test where fails: Element is not an object 2738 try 2739 { 2740 const json element = R"( 2741 [ 2742 "chassis_number", "2", 2743 "bus_number", "13" 2744 ] 2745 )"_json; 2746 parseVariables(element); 2747 ADD_FAILURE() << "Should not have reached this line."; 2748 } 2749 catch (const std::invalid_argument& e) 2750 { 2751 EXPECT_STREQ(e.what(), "Element is not an object"); 2752 } 2753 2754 // Test where fails: Key is not a string 2755 try 2756 { 2757 const json element = R"( 2758 { 2759 chassis_number: "2", 2760 "bus_number": "13" 2761 } 2762 )"_json; 2763 parseVariables(element); 2764 ADD_FAILURE() << "Should not have reached this line."; 2765 } 2766 catch (const json::parse_error& e) 2767 {} 2768 2769 // Test where fails: Value is not a string 2770 try 2771 { 2772 const json element = R"( 2773 { 2774 "chassis_number": "2", 2775 "bus_number": 13 2776 } 2777 )"_json; 2778 parseVariables(element); 2779 ADD_FAILURE() << "Should not have reached this line."; 2780 } 2781 catch (const std::invalid_argument& e) 2782 { 2783 EXPECT_STREQ(e.what(), "Element is not a string"); 2784 } 2785 2786 // Test where fails: Key is an empty string 2787 try 2788 { 2789 const json element = R"( 2790 { 2791 "chassis_number": "2", 2792 "": "13" 2793 } 2794 )"_json; 2795 parseVariables(element); 2796 ADD_FAILURE() << "Should not have reached this line."; 2797 } 2798 catch (const std::invalid_argument& e) 2799 { 2800 EXPECT_STREQ(e.what(), "Element contains an empty string"); 2801 } 2802 2803 // Test where fails: Value is an empty string 2804 try 2805 { 2806 const json element = R"( 2807 { 2808 "chassis_number": "", 2809 "bus_number": "13" 2810 } 2811 )"_json; 2812 parseVariables(element); 2813 ADD_FAILURE() << "Should not have reached this line."; 2814 } 2815 catch (const std::invalid_argument& e) 2816 { 2817 EXPECT_STREQ(e.what(), "Element contains an empty string"); 2818 } 2819 } 2820