1 /** 2 * Copyright c 2020 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 <errno.h> 17 #include <stdio.h> 18 #include <stdlib.h> 19 #include <sys/wait.h> 20 21 #include <nlohmann/json.hpp> 22 23 #include <fstream> 24 25 #include <gtest/gtest.h> 26 27 #define EXPECT_FILE_VALID(configFile) expectFileValid(configFile) 28 #define EXPECT_FILE_INVALID(configFile, expectedErrorMessage, \ 29 expectedOutputMessage) \ 30 expectFileInvalid(configFile, expectedErrorMessage, expectedOutputMessage) 31 #define EXPECT_JSON_VALID(configFileJson) expectJsonValid(configFileJson) 32 #define EXPECT_JSON_INVALID(configFileJson, expectedErrorMessage, \ 33 expectedOutputMessage) \ 34 expectJsonInvalid(configFileJson, expectedErrorMessage, \ 35 expectedOutputMessage) 36 37 using json = nlohmann::json; 38 39 const json validConfigFile = R"( 40 { 41 "comments": [ "Config file for a FooBar one-chassis system" ], 42 43 "rules": [ 44 { 45 "comments": [ "Sets output voltage for a PMBus regulator rail" ], 46 "id": "set_voltage_rule", 47 "actions": [ 48 { 49 "pmbus_write_vout_command": { 50 "format": "linear" 51 } 52 } 53 ] 54 } 55 ], 56 57 "chassis": [ 58 { 59 "comments": [ "Chassis number 1 containing CPUs and memory" ], 60 "number": 1, 61 "devices": [ 62 { 63 "comments": [ "IR35221 regulator producing the Vdd rail" ], 64 "id": "vdd_regulator", 65 "is_regulator": true, 66 "fru": "/system/chassis/motherboard/regulator1", 67 "i2c_interface": { 68 "bus": 1, 69 "address": "0x70" 70 }, 71 "rails": [ 72 { 73 "comments": [ "Vdd rail" ], 74 "id": "vdd", 75 "configuration": { 76 "volts": 1.03, 77 "rule_id": "set_voltage_rule" 78 }, 79 "sensor_monitoring": { 80 "rule_id": "read_sensors_rule" 81 } 82 } 83 ] 84 } 85 ] 86 } 87 ] 88 } 89 )"_json; 90 91 std::string createTmpFile() 92 { 93 // create temporary file using mkstemp under /tmp/. random name for XXXXXX 94 char fileName[] = "/tmp/temp-XXXXXX"; 95 int fd = mkstemp(fileName); 96 if (fd == -1) 97 { 98 perror("Can't create temporary file"); 99 } 100 close(fd); 101 return fileName; 102 } 103 104 std::string getValidationToolCommand(const std::string& configFileName) 105 { 106 std::string command = "python ../tools/validate-regulators-config.py -s \ 107 ../schema/config_schema.json -c "; 108 command += configFileName; 109 return command; 110 } 111 112 int runToolForOutput(const std::string& configFileName, std::string& output, 113 bool isReadingStderr = false) 114 { 115 // run the validation tool with the temporary file and return the output 116 // of the validation tool. 117 std::string command = getValidationToolCommand(configFileName); 118 // reading the stderr while isReadingStderr is true. 119 if (isReadingStderr == true) 120 { 121 command += " 2>&1 >/dev/null"; 122 } 123 // get the jsonschema print from validation tool. 124 char buffer[256]; 125 std::string result = ""; 126 // to get the stdout from the validation tool. 127 FILE* pipe = popen(command.c_str(), "r"); 128 if (!pipe) 129 { 130 throw std::runtime_error("popen() failed!"); 131 } 132 while (!std::feof(pipe)) 133 { 134 if (fgets(buffer, sizeof buffer, pipe) != NULL) 135 { 136 result += buffer; 137 } 138 } 139 int returnValue = pclose(pipe); 140 // Check if pclose() failed 141 if (returnValue == -1) 142 { 143 // unable to close pipe. Print error and exit function. 144 throw std::runtime_error("pclose() failed!"); 145 } 146 std::string firstLine = result.substr(0, result.find('\n')); 147 output = firstLine; 148 // Get command exit status from return value 149 int exitStatus = WEXITSTATUS(returnValue); 150 return exitStatus; 151 } 152 153 void expectFileValid(const std::string& configFileName) 154 { 155 std::string errorMessage = ""; 156 std::string outputMessage = ""; 157 EXPECT_EQ(runToolForOutput(configFileName, errorMessage, true), 0); 158 EXPECT_EQ(runToolForOutput(configFileName, outputMessage), 0); 159 EXPECT_EQ(errorMessage, ""); 160 EXPECT_EQ(outputMessage, ""); 161 } 162 163 void expectFileInvalid(const std::string& configFileName, 164 const std::string& expectedErrorMessage, 165 const std::string& expectedOutputMessage) 166 { 167 std::string errorMessage = ""; 168 std::string outputMessage = ""; 169 EXPECT_EQ(runToolForOutput(configFileName, errorMessage, true), 1); 170 EXPECT_EQ(runToolForOutput(configFileName, outputMessage), 1); 171 EXPECT_EQ(errorMessage, expectedErrorMessage); 172 EXPECT_EQ(outputMessage, expectedOutputMessage); 173 } 174 175 void expectJsonValid(const json configFileJson) 176 { 177 std::string fileName; 178 fileName = createTmpFile(); 179 std::string jsonData = configFileJson.dump(); 180 std::ofstream out(fileName); 181 out << jsonData; 182 out.close(); 183 184 EXPECT_FILE_VALID(fileName); 185 unlink(fileName.c_str()); 186 } 187 188 void expectJsonInvalid(const json configFileJson, 189 const std::string& expectedErrorMessage, 190 const std::string& expectedOutputMessage) 191 { 192 std::string fileName; 193 fileName = createTmpFile(); 194 std::string jsonData = configFileJson.dump(); 195 std::ofstream out(fileName); 196 out << jsonData; 197 out.close(); 198 199 EXPECT_FILE_INVALID(fileName, expectedErrorMessage, expectedOutputMessage); 200 unlink(fileName.c_str()); 201 } 202 203 TEST(ValidateRegulatorsConfigTest, Rule) 204 { 205 // valid test comments property, id property, 206 // action property specified. 207 { 208 json configFile = validConfigFile; 209 EXPECT_JSON_VALID(configFile); 210 } 211 212 // valid test rule with no comments 213 { 214 json configFile = validConfigFile; 215 configFile["rules"][0].erase("comments"); 216 EXPECT_JSON_VALID(configFile); 217 } 218 219 // invalid test comments property has invalid value type 220 { 221 json configFile = validConfigFile; 222 configFile["rules"][0]["comments"] = {1}; 223 EXPECT_JSON_INVALID(configFile, "Validation failed.", 224 "1 is not of type u'string'"); 225 } 226 227 // invalid test rule with no ID 228 { 229 json configFile = validConfigFile; 230 configFile["rules"][0].erase("id"); 231 EXPECT_JSON_INVALID(configFile, "Validation failed.", 232 "u'id' is a required property"); 233 } 234 235 // invalid test id property has invalid value type (not string) 236 { 237 json configFile = validConfigFile; 238 configFile["rules"][0]["id"] = true; 239 EXPECT_JSON_INVALID(configFile, "Validation failed.", 240 "True is not of type u'string'"); 241 } 242 243 // invalid test id property has invalid value 244 { 245 json configFile = validConfigFile; 246 configFile["rules"][0]["id"] = "foo%"; 247 EXPECT_JSON_INVALID(configFile, "Validation failed.", 248 "u'foo%' does not match u'^[A-Za-z0-9_]+$'"); 249 } 250 251 // invalid test rule with no actions property 252 { 253 json configFile = validConfigFile; 254 configFile["rules"][0].erase("actions"); 255 EXPECT_JSON_INVALID(configFile, "Validation failed.", 256 "u'actions' is a required property"); 257 } 258 259 // valid test rule with multiple actions 260 { 261 json configFile = validConfigFile; 262 configFile["rules"][0]["actions"][1]["run_rule"] = 263 "set_page0_voltage_rule"; 264 EXPECT_JSON_VALID(configFile); 265 } 266 267 // invalid test actions property has invalid value type (not an array) 268 { 269 json configFile = validConfigFile; 270 configFile["rules"][0]["actions"] = 1; 271 EXPECT_JSON_INVALID(configFile, "Validation failed.", 272 "1 is not of type u'array'"); 273 } 274 275 // invalid test actions property has invalid value of action 276 { 277 json configFile = validConfigFile; 278 configFile["rules"][0]["actions"][0] = "foo"; 279 EXPECT_JSON_INVALID(configFile, "Validation failed.", 280 "u'foo' is not of type u'object'"); 281 } 282 283 // invalid test actions property has empty array 284 { 285 json configFile = validConfigFile; 286 configFile["rules"][0]["actions"] = json::array(); 287 EXPECT_JSON_INVALID(configFile, "Validation failed.", 288 "[] is too short"); 289 } 290 } 291 TEST(ValidateRegulatorsConfigTest, And) 292 { 293 // Valid. 294 { 295 json configFile = validConfigFile; 296 json andAction = 297 R"( 298 { 299 "and": [ 300 { "i2c_compare_byte": { "register": "0xA0", "value": "0x00" } }, 301 { "i2c_compare_byte": { "register": "0xA1", "value": "0x00" } } 302 ] 303 } 304 )"_json; 305 configFile["rules"][0]["actions"].push_back(andAction); 306 EXPECT_JSON_VALID(configFile); 307 } 308 309 // Invalid: actions property value is an empty array. 310 { 311 json configFile = validConfigFile; 312 json andAction = 313 R"( 314 { 315 "and": [] 316 } 317 )"_json; 318 configFile["rules"][0]["actions"].push_back(andAction); 319 EXPECT_JSON_INVALID(configFile, "Validation failed.", 320 "[] is too short"); 321 } 322 323 // Invalid: actions property has incorrect value data type. 324 { 325 json configFile = validConfigFile; 326 json andAction = 327 R"( 328 { 329 "and": true 330 } 331 )"_json; 332 configFile["rules"][0]["actions"].push_back(andAction); 333 EXPECT_JSON_INVALID(configFile, "Validation failed.", 334 "True is not of type u'array'"); 335 } 336 337 // Invalid: actions property value contains wrong element type 338 { 339 json configFile = validConfigFile; 340 json andAction = 341 R"( 342 { 343 "and": ["foo"] 344 } 345 )"_json; 346 configFile["rules"][0]["actions"].push_back(andAction); 347 EXPECT_JSON_INVALID(configFile, "Validation failed.", 348 "u'foo' is not of type u'object'"); 349 } 350 } 351 TEST(ValidateRegulatorsConfigTest, ComparePresence) 352 { 353 json comparePresenceFile = validConfigFile; 354 comparePresenceFile["rules"][0]["actions"][1]["compare_presence"]["fru"] = 355 "/system/chassis/motherboard/regulator2"; 356 comparePresenceFile["rules"][0]["actions"][1]["compare_presence"]["value"] = 357 true; 358 // Valid. 359 { 360 json configFile = comparePresenceFile; 361 EXPECT_JSON_VALID(configFile); 362 } 363 364 // Invalid: no FRU property. 365 { 366 json configFile = comparePresenceFile; 367 configFile["rules"][0]["actions"][1]["compare_presence"].erase("fru"); 368 EXPECT_JSON_INVALID(configFile, "Validation failed.", 369 "u'fru' is a required property"); 370 } 371 372 // Invalid: FRU property length is string less than 1. 373 { 374 json configFile = comparePresenceFile; 375 configFile["rules"][0]["actions"][1]["compare_presence"]["fru"] = ""; 376 EXPECT_JSON_INVALID(configFile, "Validation failed.", 377 "u'' is too short"); 378 } 379 380 // Invalid: no value property. 381 { 382 json configFile = comparePresenceFile; 383 configFile["rules"][0]["actions"][1]["compare_presence"].erase("value"); 384 EXPECT_JSON_INVALID(configFile, "Validation failed.", 385 "u'value' is a required property"); 386 } 387 388 // Invalid: value property type is not boolean. 389 { 390 json configFile = comparePresenceFile; 391 configFile["rules"][0]["actions"][1]["compare_presence"]["value"] = "1"; 392 EXPECT_JSON_INVALID(configFile, "Validation failed.", 393 "u'1' is not of type u'boolean'"); 394 } 395 396 // Invalid: FRU property type is not string. 397 { 398 json configFile = comparePresenceFile; 399 configFile["rules"][0]["actions"][1]["compare_presence"]["fru"] = 1; 400 EXPECT_JSON_INVALID(configFile, "Validation failed.", 401 "1 is not of type u'string'"); 402 } 403 } 404 TEST(ValidateRegulatorsConfigTest, CompareVpd) 405 { 406 json compareVpdFile = validConfigFile; 407 compareVpdFile["rules"][0]["actions"][1]["compare_vpd"]["fru"] = 408 "/system/chassis/motherboard/regulator2"; 409 compareVpdFile["rules"][0]["actions"][1]["compare_vpd"]["keyword"] = "CCIN"; 410 compareVpdFile["rules"][0]["actions"][1]["compare_vpd"]["value"] = "2D35"; 411 412 // Valid. 413 { 414 json configFile = compareVpdFile; 415 EXPECT_JSON_VALID(configFile); 416 } 417 418 // Invalid: no FRU property. 419 { 420 json configFile = compareVpdFile; 421 configFile["rules"][0]["actions"][1]["compare_vpd"].erase("fru"); 422 EXPECT_JSON_INVALID(configFile, "Validation failed.", 423 "u'fru' is a required property"); 424 } 425 426 // Invalid: no keyword property. 427 { 428 json configFile = compareVpdFile; 429 configFile["rules"][0]["actions"][1]["compare_vpd"].erase("keyword"); 430 EXPECT_JSON_INVALID(configFile, "Validation failed.", 431 "u'keyword' is a required property"); 432 } 433 434 // Invalid: no value property. 435 { 436 json configFile = compareVpdFile; 437 configFile["rules"][0]["actions"][1]["compare_vpd"].erase("value"); 438 EXPECT_JSON_INVALID(configFile, "Validation failed.", 439 "u'value' is a required property"); 440 } 441 442 // Invalid: property FRU wrong type. 443 { 444 json configFile = compareVpdFile; 445 configFile["rules"][0]["actions"][1]["compare_vpd"]["fru"] = 1; 446 EXPECT_JSON_INVALID(configFile, "Validation failed.", 447 "1 is not of type u'string'"); 448 } 449 450 // Invalid: property FRU is string less than 1. 451 { 452 json configFile = compareVpdFile; 453 configFile["rules"][0]["actions"][1]["compare_vpd"]["fru"] = ""; 454 EXPECT_JSON_INVALID(configFile, "Validation failed.", 455 "u'' is too short"); 456 } 457 458 // Invalid: property keyword is not "CCIN", "Manufacturer", "Model", 459 // "PartNumber" 460 { 461 json configFile = compareVpdFile; 462 configFile["rules"][0]["actions"][1]["compare_vpd"]["keyword"] = 463 "Number"; 464 EXPECT_JSON_INVALID(configFile, "Validation failed.", 465 "u'Number' is not one of [u'CCIN', " 466 "u'Manufacturer', u'Model', u'PartNumber']"); 467 } 468 469 // Invalid: property value wrong type. 470 { 471 json configFile = compareVpdFile; 472 configFile["rules"][0]["actions"][1]["compare_vpd"]["value"] = 1; 473 EXPECT_JSON_INVALID(configFile, "Validation failed.", 474 "1 is not of type u'string'"); 475 } 476 } 477 TEST(ValidateRegulatorsConfigTest, I2CCompareBit) 478 { 479 json i2cCompareBitFile = validConfigFile; 480 i2cCompareBitFile["rules"][0]["actions"][1]["i2c_compare_bit"]["register"] = 481 "0xA0"; 482 i2cCompareBitFile["rules"][0]["actions"][1]["i2c_compare_bit"]["position"] = 483 3; 484 i2cCompareBitFile["rules"][0]["actions"][1]["i2c_compare_bit"]["value"] = 1; 485 // Valid: test rule actions i2c_compare_bit. 486 { 487 json configFile = i2cCompareBitFile; 488 EXPECT_JSON_VALID(configFile); 489 } 490 // Invalid: test i2c_compare_bit with no register. 491 { 492 json configFile = i2cCompareBitFile; 493 configFile["rules"][0]["actions"][1]["i2c_compare_bit"].erase( 494 "register"); 495 EXPECT_JSON_INVALID(configFile, "Validation failed.", 496 "u'register' is a required property"); 497 } 498 // Invalid: test i2c_compare_bit with no position. 499 { 500 json configFile = i2cCompareBitFile; 501 configFile["rules"][0]["actions"][1]["i2c_compare_bit"].erase( 502 "position"); 503 EXPECT_JSON_INVALID(configFile, "Validation failed.", 504 "u'position' is a required property"); 505 } 506 // Invalid: test i2c_compare_bit with no value. 507 { 508 json configFile = i2cCompareBitFile; 509 configFile["rules"][0]["actions"][1]["i2c_compare_bit"].erase("value"); 510 EXPECT_JSON_INVALID(configFile, "Validation failed.", 511 "u'value' is a required property"); 512 } 513 // Invalid: test i2c_compare_bit with register wrong type. 514 { 515 json configFile = i2cCompareBitFile; 516 configFile["rules"][0]["actions"][1]["i2c_compare_bit"]["register"] = 1; 517 EXPECT_JSON_INVALID(configFile, "Validation failed.", 518 "1 is not of type u'string'"); 519 } 520 // Invalid: test i2c_compare_bit with register wrong format. 521 { 522 json configFile = i2cCompareBitFile; 523 configFile["rules"][0]["actions"][1]["i2c_compare_bit"]["register"] = 524 "0xA00"; 525 EXPECT_JSON_INVALID(configFile, "Validation failed.", 526 "u'0xA00' does not match u'^0x[0-9A-Fa-f]{2}$'"); 527 } 528 // Invalid: test i2c_compare_bit with position wrong type. 529 { 530 json configFile = i2cCompareBitFile; 531 configFile["rules"][0]["actions"][1]["i2c_compare_bit"]["position"] = 532 3.1; 533 EXPECT_JSON_INVALID(configFile, "Validation failed.", 534 "3.1 is not of type u'integer'"); 535 } 536 // Invalid: test i2c_compare_bit with position greater than 7. 537 { 538 json configFile = i2cCompareBitFile; 539 configFile["rules"][0]["actions"][1]["i2c_compare_bit"]["position"] = 8; 540 EXPECT_JSON_INVALID(configFile, "Validation failed.", 541 "8 is greater than the maximum of 7"); 542 } 543 // Invalid: test i2c_compare_bit with position less than 0. 544 { 545 json configFile = i2cCompareBitFile; 546 configFile["rules"][0]["actions"][1]["i2c_compare_bit"]["position"] = 547 -1; 548 EXPECT_JSON_INVALID(configFile, "Validation failed.", 549 "-1 is less than the minimum of 0"); 550 } 551 // Invalid: test i2c_compare_bit with value wrong type. 552 { 553 json configFile = i2cCompareBitFile; 554 configFile["rules"][0]["actions"][1]["i2c_compare_bit"]["value"] = "1"; 555 EXPECT_JSON_INVALID(configFile, "Validation failed.", 556 "u'1' is not of type u'integer'"); 557 } 558 // Invalid: test i2c_compare_bit with value greater than 1. 559 { 560 json configFile = i2cCompareBitFile; 561 configFile["rules"][0]["actions"][1]["i2c_compare_bit"]["value"] = 2; 562 EXPECT_JSON_INVALID(configFile, "Validation failed.", 563 "2 is greater than the maximum of 1"); 564 } 565 // Invalid: test i2c_compare_bit with value less than 0. 566 { 567 json configFile = i2cCompareBitFile; 568 configFile["rules"][0]["actions"][1]["i2c_compare_bit"]["value"] = -1; 569 EXPECT_JSON_INVALID(configFile, "Validation failed.", 570 "-1 is less than the minimum of 0"); 571 } 572 } 573 TEST(ValidateRegulatorsConfigTest, I2CCompareByte) 574 { 575 json i2cCompareByteFile = validConfigFile; 576 i2cCompareByteFile["rules"][0]["actions"][1]["i2c_compare_byte"] 577 ["register"] = "0x82"; 578 i2cCompareByteFile["rules"][0]["actions"][1]["i2c_compare_byte"]["value"] = 579 "0x40"; 580 i2cCompareByteFile["rules"][0]["actions"][1]["i2c_compare_byte"]["mask"] = 581 "0x7F"; 582 // Valid: test i2c_compare_byte with all properties. 583 { 584 json configFile = i2cCompareByteFile; 585 EXPECT_JSON_VALID(configFile); 586 } 587 // Valid: test i2c_compare_byte with all required properties. 588 { 589 json configFile = i2cCompareByteFile; 590 configFile["rules"][0]["actions"][1]["i2c_compare_byte"].erase("mask"); 591 EXPECT_JSON_VALID(configFile); 592 } 593 // Invalid: test i2c_compare_byte with no register. 594 { 595 json configFile = i2cCompareByteFile; 596 configFile["rules"][0]["actions"][1]["i2c_compare_byte"].erase( 597 "register"); 598 EXPECT_JSON_INVALID(configFile, "Validation failed.", 599 "u'register' is a required property"); 600 } 601 // Invalid: test i2c_compare_byte with no value. 602 { 603 json configFile = i2cCompareByteFile; 604 configFile["rules"][0]["actions"][1]["i2c_compare_byte"].erase("value"); 605 EXPECT_JSON_INVALID(configFile, "Validation failed.", 606 "u'value' is a required property"); 607 } 608 // Invalid: test i2c_compare_byte with property register wrong type. 609 { 610 json configFile = i2cCompareByteFile; 611 configFile["rules"][0]["actions"][1]["i2c_compare_byte"]["register"] = 612 1; 613 EXPECT_JSON_INVALID(configFile, "Validation failed.", 614 "1 is not of type u'string'"); 615 } 616 // Invalid: test i2c_compare_byte with property value wrong type. 617 { 618 json configFile = i2cCompareByteFile; 619 configFile["rules"][0]["actions"][1]["i2c_compare_byte"]["value"] = 1; 620 EXPECT_JSON_INVALID(configFile, "Validation failed.", 621 "1 is not of type u'string'"); 622 } 623 // Invalid: test i2c_compare_byte with property mask wrong type. 624 { 625 json configFile = i2cCompareByteFile; 626 configFile["rules"][0]["actions"][1]["i2c_compare_byte"]["mask"] = 1; 627 EXPECT_JSON_INVALID(configFile, "Validation failed.", 628 "1 is not of type u'string'"); 629 } 630 // Invalid: test i2c_compare_byte with property register more than 2 hex 631 // digits. 632 { 633 json configFile = i2cCompareByteFile; 634 configFile["rules"][0]["actions"][1]["i2c_compare_byte"]["register"] = 635 "0x820"; 636 EXPECT_JSON_INVALID(configFile, "Validation failed.", 637 "u'0x820' does not match u'^0x[0-9A-Fa-f]{2}$'"); 638 } 639 // Invalid: test i2c_compare_byte with property value more than 2 hex 640 // digits. 641 { 642 json configFile = i2cCompareByteFile; 643 configFile["rules"][0]["actions"][1]["i2c_compare_byte"]["value"] = 644 "0x820"; 645 EXPECT_JSON_INVALID(configFile, "Validation failed.", 646 "u'0x820' does not match u'^0x[0-9A-Fa-f]{2}$'"); 647 } 648 // Invalid: test i2c_compare_byte with property mask more than 2 hex digits. 649 { 650 json configFile = i2cCompareByteFile; 651 configFile["rules"][0]["actions"][1]["i2c_compare_byte"]["mask"] = 652 "0x820"; 653 EXPECT_JSON_INVALID(configFile, "Validation failed.", 654 "u'0x820' does not match u'^0x[0-9A-Fa-f]{2}$'"); 655 } 656 // Invalid: test i2c_compare_byte with property register less than 2 hex 657 // digits. 658 { 659 json configFile = i2cCompareByteFile; 660 configFile["rules"][0]["actions"][1]["i2c_compare_byte"]["register"] = 661 "0x8"; 662 EXPECT_JSON_INVALID(configFile, "Validation failed.", 663 "u'0x8' does not match u'^0x[0-9A-Fa-f]{2}$'"); 664 } 665 // Invalid: test i2c_compare_byte with property value less than 2 hex 666 // digits. 667 { 668 json configFile = i2cCompareByteFile; 669 configFile["rules"][0]["actions"][1]["i2c_compare_byte"]["value"] = 670 "0x8"; 671 EXPECT_JSON_INVALID(configFile, "Validation failed.", 672 "u'0x8' does not match u'^0x[0-9A-Fa-f]{2}$'"); 673 } 674 // Invalid: test i2c_compare_byte with property mask less than 2 hex digits. 675 { 676 json configFile = i2cCompareByteFile; 677 configFile["rules"][0]["actions"][1]["i2c_compare_byte"]["mask"] = 678 "0x8"; 679 EXPECT_JSON_INVALID(configFile, "Validation failed.", 680 "u'0x8' does not match u'^0x[0-9A-Fa-f]{2}$'"); 681 } 682 // Invalid: test i2c_compare_byte with property register no leading prefix. 683 { 684 json configFile = i2cCompareByteFile; 685 configFile["rules"][0]["actions"][1]["i2c_compare_byte"]["register"] = 686 "82"; 687 EXPECT_JSON_INVALID(configFile, "Validation failed.", 688 "u'82' does not match u'^0x[0-9A-Fa-f]{2}$'"); 689 } 690 // Invalid: test i2c_compare_byte with property value no leading prefix. 691 { 692 json configFile = i2cCompareByteFile; 693 configFile["rules"][0]["actions"][1]["i2c_compare_byte"]["value"] = 694 "82"; 695 EXPECT_JSON_INVALID(configFile, "Validation failed.", 696 "u'82' does not match u'^0x[0-9A-Fa-f]{2}$'"); 697 } 698 // Invalid: test i2c_compare_byte with property mask no leading prefix. 699 { 700 json configFile = i2cCompareByteFile; 701 configFile["rules"][0]["actions"][1]["i2c_compare_byte"]["mask"] = "82"; 702 EXPECT_JSON_INVALID(configFile, "Validation failed.", 703 "u'82' does not match u'^0x[0-9A-Fa-f]{2}$'"); 704 } 705 // Invalid: test i2c_compare_byte with property register invalid hex digit. 706 { 707 json configFile = i2cCompareByteFile; 708 configFile["rules"][0]["actions"][1]["i2c_compare_byte"]["register"] = 709 "0xG1"; 710 EXPECT_JSON_INVALID(configFile, "Validation failed.", 711 "u'0xG1' does not match u'^0x[0-9A-Fa-f]{2}$'"); 712 } 713 // Invalid: test i2c_compare_byte with property value invalid hex digit. 714 { 715 json configFile = i2cCompareByteFile; 716 configFile["rules"][0]["actions"][1]["i2c_compare_byte"]["value"] = 717 "0xG1"; 718 EXPECT_JSON_INVALID(configFile, "Validation failed.", 719 "u'0xG1' does not match u'^0x[0-9A-Fa-f]{2}$'"); 720 } 721 // Invalid: test i2c_compare_byte with property mask invalid hex digit. 722 { 723 json configFile = i2cCompareByteFile; 724 configFile["rules"][0]["actions"][1]["i2c_compare_byte"]["mask"] = 725 "0xG1"; 726 EXPECT_JSON_INVALID(configFile, "Validation failed.", 727 "u'0xG1' does not match u'^0x[0-9A-Fa-f]{2}$'"); 728 } 729 } 730 TEST(ValidateRegulatorsConfigTest, I2CCompareBytes) 731 { 732 json i2cCompareBytesFile = validConfigFile; 733 i2cCompareBytesFile["rules"][0]["actions"][1]["i2c_compare_bytes"] 734 ["register"] = "0x82"; 735 i2cCompareBytesFile["rules"][0]["actions"][1]["i2c_compare_bytes"] 736 ["values"] = {"0x02", "0x73"}; 737 i2cCompareBytesFile["rules"][0]["actions"][1]["i2c_compare_bytes"] 738 ["masks"] = {"0x7F", "0x7F"}; 739 // Valid: test i2c_compare_bytes. 740 { 741 json configFile = i2cCompareBytesFile; 742 EXPECT_JSON_VALID(configFile); 743 } 744 // Valid: test i2c_compare_bytes with all required properties. 745 { 746 json configFile = i2cCompareBytesFile; 747 configFile["rules"][0]["actions"][1]["i2c_compare_bytes"].erase( 748 "masks"); 749 EXPECT_JSON_VALID(configFile); 750 } 751 // Invalid: test i2c_compare_bytes with no register. 752 { 753 json configFile = i2cCompareBytesFile; 754 configFile["rules"][0]["actions"][1]["i2c_compare_bytes"].erase( 755 "register"); 756 EXPECT_JSON_INVALID(configFile, "Validation failed.", 757 "u'register' is a required property"); 758 } 759 // Invalid: test i2c_compare_bytes with no values. 760 { 761 json configFile = i2cCompareBytesFile; 762 configFile["rules"][0]["actions"][1]["i2c_compare_bytes"].erase( 763 "values"); 764 EXPECT_JSON_INVALID(configFile, "Validation failed.", 765 "u'values' is a required property"); 766 } 767 // Invalid: test i2c_compare_bytes with property values as empty array. 768 { 769 json configFile = i2cCompareBytesFile; 770 configFile["rules"][0]["actions"][1]["i2c_compare_bytes"]["values"] = 771 json::array(); 772 EXPECT_JSON_INVALID(configFile, "Validation failed.", 773 "[] is too short"); 774 } 775 // Invalid: test i2c_compare_bytes with property masks as empty array. 776 { 777 json configFile = i2cCompareBytesFile; 778 configFile["rules"][0]["actions"][1]["i2c_compare_bytes"]["masks"] = 779 json::array(); 780 EXPECT_JSON_INVALID(configFile, "Validation failed.", 781 "[] is too short"); 782 } 783 // Invalid: test i2c_compare_bytes with property register wrong type. 784 { 785 json configFile = i2cCompareBytesFile; 786 configFile["rules"][0]["actions"][1]["i2c_compare_bytes"]["register"] = 787 1; 788 EXPECT_JSON_INVALID(configFile, "Validation failed.", 789 "1 is not of type u'string'"); 790 } 791 // Invalid: test i2c_compare_bytes with property values wrong type. 792 { 793 json configFile = i2cCompareBytesFile; 794 configFile["rules"][0]["actions"][1]["i2c_compare_bytes"]["values"] = 1; 795 EXPECT_JSON_INVALID(configFile, "Validation failed.", 796 "1 is not of type u'array'"); 797 } 798 // Invalid: test i2c_compare_bytes with property masks wrong type. 799 { 800 json configFile = i2cCompareBytesFile; 801 configFile["rules"][0]["actions"][1]["i2c_compare_bytes"]["masks"] = 1; 802 EXPECT_JSON_INVALID(configFile, "Validation failed.", 803 "1 is not of type u'array'"); 804 } 805 // Invalid: test i2c_compare_bytes with property register more than 2 hex 806 // digits. 807 { 808 json configFile = i2cCompareBytesFile; 809 configFile["rules"][0]["actions"][1]["i2c_compare_bytes"]["register"] = 810 "0x820"; 811 EXPECT_JSON_INVALID(configFile, "Validation failed.", 812 "u'0x820' does not match u'^0x[0-9A-Fa-f]{2}$'"); 813 } 814 // Invalid: test i2c_compare_bytes with property values more than 2 hex 815 // digits. 816 { 817 json configFile = i2cCompareBytesFile; 818 configFile["rules"][0]["actions"][1]["i2c_compare_bytes"]["values"][0] = 819 "0x820"; 820 EXPECT_JSON_INVALID(configFile, "Validation failed.", 821 "u'0x820' does not match u'^0x[0-9A-Fa-f]{2}$'"); 822 } 823 // Invalid: test i2c_compare_bytes with property masks more than 2 hex 824 // digits. 825 { 826 json configFile = i2cCompareBytesFile; 827 configFile["rules"][0]["actions"][1]["i2c_compare_bytes"]["masks"][0] = 828 "0x820"; 829 EXPECT_JSON_INVALID(configFile, "Validation failed.", 830 "u'0x820' does not match u'^0x[0-9A-Fa-f]{2}$'"); 831 } 832 // Invalid: test i2c_compare_bytes with property register less than 2 hex 833 // digits. 834 { 835 json configFile = i2cCompareBytesFile; 836 configFile["rules"][0]["actions"][1]["i2c_compare_bytes"]["register"] = 837 "0x8"; 838 EXPECT_JSON_INVALID(configFile, "Validation failed.", 839 "u'0x8' does not match u'^0x[0-9A-Fa-f]{2}$'"); 840 } 841 // Invalid: test i2c_compare_bytes with property values less than 2 hex 842 // digits. 843 { 844 json configFile = i2cCompareBytesFile; 845 configFile["rules"][0]["actions"][1]["i2c_compare_bytes"]["values"][0] = 846 "0x8"; 847 EXPECT_JSON_INVALID(configFile, "Validation failed.", 848 "u'0x8' does not match u'^0x[0-9A-Fa-f]{2}$'"); 849 } 850 // Invalid: test i2c_compare_bytes with property masks less than 2 hex 851 // digits. 852 { 853 json configFile = i2cCompareBytesFile; 854 configFile["rules"][0]["actions"][1]["i2c_compare_bytes"]["masks"][0] = 855 "0x8"; 856 EXPECT_JSON_INVALID(configFile, "Validation failed.", 857 "u'0x8' does not match u'^0x[0-9A-Fa-f]{2}$'"); 858 } 859 // Invalid: test i2c_compare_bytes with property register no leading prefix. 860 { 861 json configFile = i2cCompareBytesFile; 862 configFile["rules"][0]["actions"][1]["i2c_compare_bytes"]["register"] = 863 "82"; 864 EXPECT_JSON_INVALID(configFile, "Validation failed.", 865 "u'82' does not match u'^0x[0-9A-Fa-f]{2}$'"); 866 } 867 // Invalid: test i2c_compare_bytes with property values no leading prefix. 868 { 869 json configFile = i2cCompareBytesFile; 870 configFile["rules"][0]["actions"][1]["i2c_compare_bytes"]["values"][0] = 871 "82"; 872 EXPECT_JSON_INVALID(configFile, "Validation failed.", 873 "u'82' does not match u'^0x[0-9A-Fa-f]{2}$'"); 874 } 875 // Invalid: test i2c_compare_bytes with property masks no leading prefix. 876 { 877 json configFile = i2cCompareBytesFile; 878 configFile["rules"][0]["actions"][1]["i2c_compare_bytes"]["masks"][0] = 879 "82"; 880 EXPECT_JSON_INVALID(configFile, "Validation failed.", 881 "u'82' does not match u'^0x[0-9A-Fa-f]{2}$'"); 882 } 883 // Invalid: test i2c_compare_bytes with property register invalid hex digit. 884 { 885 json configFile = i2cCompareBytesFile; 886 configFile["rules"][0]["actions"][1]["i2c_compare_bytes"]["register"] = 887 "0xG1"; 888 EXPECT_JSON_INVALID(configFile, "Validation failed.", 889 "u'0xG1' does not match u'^0x[0-9A-Fa-f]{2}$'"); 890 } 891 // Invalid: test i2c_compare_bytes with property values invalid hex digit. 892 { 893 json configFile = i2cCompareBytesFile; 894 configFile["rules"][0]["actions"][1]["i2c_compare_bytes"]["values"][0] = 895 "0xG1"; 896 EXPECT_JSON_INVALID(configFile, "Validation failed.", 897 "u'0xG1' does not match u'^0x[0-9A-Fa-f]{2}$'"); 898 } 899 // Invalid: test i2c_compare_bytes with property masks invalid hex digit. 900 { 901 json configFile = i2cCompareBytesFile; 902 configFile["rules"][0]["actions"][1]["i2c_compare_bytes"]["masks"][0] = 903 "0xG1"; 904 EXPECT_JSON_INVALID(configFile, "Validation failed.", 905 "u'0xG1' does not match u'^0x[0-9A-Fa-f]{2}$'"); 906 } 907 } 908 TEST(ValidateRegulatorsConfigTest, If) 909 { 910 json ifFile = validConfigFile; 911 ifFile["rules"][0]["actions"][1]["if"]["condition"]["run_rule"] = 912 "is_downlevel_regulator"; 913 ifFile["rules"][0]["actions"][1]["if"]["then"][0]["run_rule"] = 914 "configure_downlevel_regulator"; 915 ifFile["rules"][0]["actions"][1]["if"]["else"][0]["run_rule"] = 916 "configure_downlevel_regulator"; 917 // Valid: test if. 918 { 919 json configFile = ifFile; 920 EXPECT_JSON_VALID(configFile); 921 } 922 // Valid: test if with required properties. 923 { 924 json configFile = ifFile; 925 configFile["rules"][0]["actions"][1]["if"].erase("else"); 926 EXPECT_JSON_VALID(configFile); 927 } 928 // Invalid: test if with no property condition. 929 { 930 json configFile = ifFile; 931 configFile["rules"][0]["actions"][1]["if"].erase("condition"); 932 EXPECT_JSON_INVALID(configFile, "Validation failed.", 933 "u'condition' is a required property"); 934 } 935 // Invalid: test if with no property then. 936 { 937 json configFile = ifFile; 938 configFile["rules"][0]["actions"][1]["if"].erase("then"); 939 EXPECT_JSON_INVALID(configFile, "Validation failed.", 940 "u'then' is a required property"); 941 } 942 // Invalid: test if with property then empty array. 943 { 944 json configFile = ifFile; 945 configFile["rules"][0]["actions"][1]["if"]["then"] = json::array(); 946 EXPECT_JSON_INVALID(configFile, "Validation failed.", 947 "[] is too short"); 948 } 949 // Invalid: test if with property else empty array. 950 { 951 json configFile = ifFile; 952 configFile["rules"][0]["actions"][1]["if"]["else"] = json::array(); 953 EXPECT_JSON_INVALID(configFile, "Validation failed.", 954 "[] is too short"); 955 } 956 // Invalid: test if with property condition wrong type. 957 { 958 json configFile = ifFile; 959 configFile["rules"][0]["actions"][1]["if"]["condition"] = 1; 960 EXPECT_JSON_INVALID(configFile, "Validation failed.", 961 "1 is not of type u'object'"); 962 } 963 // Invalid: test if with property then wrong type. 964 { 965 json configFile = ifFile; 966 configFile["rules"][0]["actions"][1]["if"]["then"] = 1; 967 EXPECT_JSON_INVALID(configFile, "Validation failed.", 968 "1 is not of type u'array'"); 969 } 970 // Invalid: test if with property else wrong type. 971 { 972 json configFile = ifFile; 973 configFile["rules"][0]["actions"][1]["if"]["else"] = 1; 974 EXPECT_JSON_INVALID(configFile, "Validation failed.", 975 "1 is not of type u'array'"); 976 } 977 } 978 TEST(ValidateRegulatorsConfigTest, Not) 979 { 980 json notFile = validConfigFile; 981 notFile["rules"][0]["actions"][1]["not"]["i2c_compare_byte"]["register"] = 982 "0xA0"; 983 notFile["rules"][0]["actions"][1]["not"]["i2c_compare_byte"]["value"] = 984 "0xFF"; 985 // Valid: test not. 986 { 987 json configFile = notFile; 988 EXPECT_JSON_VALID(configFile); 989 } 990 // Invalid: test not with wrong type. 991 { 992 json configFile = notFile; 993 configFile["rules"][0]["actions"][1]["not"] = 1; 994 EXPECT_JSON_INVALID(configFile, "Validation failed.", 995 "1 is not of type u'object'"); 996 } 997 } 998 TEST(ValidateRegulatorsConfigTest, Or) 999 { 1000 json orFile = validConfigFile; 1001 orFile["rules"][0]["actions"][1]["or"][0]["i2c_compare_byte"]["register"] = 1002 "0xA0"; 1003 orFile["rules"][0]["actions"][1]["or"][0]["i2c_compare_byte"]["value"] = 1004 "0x00"; 1005 orFile["rules"][0]["actions"][1]["or"][1]["i2c_compare_byte"]["register"] = 1006 "0xA1"; 1007 orFile["rules"][0]["actions"][1]["or"][1]["i2c_compare_byte"]["value"] = 1008 "0x00"; 1009 // Valid: test or. 1010 { 1011 json configFile = orFile; 1012 EXPECT_JSON_VALID(configFile); 1013 } 1014 // Invalid: test or with empty array. 1015 { 1016 json configFile = orFile; 1017 configFile["rules"][0]["actions"][1]["or"] = json::array(); 1018 EXPECT_JSON_INVALID(configFile, "Validation failed.", 1019 "[] is too short"); 1020 } 1021 // Invalid: test or with wrong type. 1022 { 1023 json configFile = orFile; 1024 configFile["rules"][0]["actions"][1]["or"] = 1; 1025 EXPECT_JSON_INVALID(configFile, "Validation failed.", 1026 "1 is not of type u'array'"); 1027 } 1028 } 1029