/** * Copyright c 2020 IBM Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "temporary_file.hpp" #include // for popen(), pclose(), fgets() #include // for chmod() #include // for WEXITSTATUS #include #include #include #include #define EXPECT_FILE_VALID(configFile) expectFileValid(configFile) #define EXPECT_FILE_INVALID(configFile, expectedErrorMessage, \ expectedOutputMessage) \ expectFileInvalid(configFile, expectedErrorMessage, expectedOutputMessage) #define EXPECT_JSON_VALID(configFileJson) expectJsonValid(configFileJson) #define EXPECT_JSON_INVALID(configFileJson, expectedErrorMessage, \ expectedOutputMessage) \ expectJsonInvalid(configFileJson, expectedErrorMessage, \ expectedOutputMessage) using namespace phosphor::power::regulators; using json = nlohmann::json; const json validConfigFile = R"( { "comments": [ "Config file for a FooBar one-chassis system" ], "rules": [ { "comments": [ "Sets output voltage for a PMBus regulator rail" ], "id": "set_voltage_rule", "actions": [ { "pmbus_write_vout_command": { "format": "linear" } } ] }, { "comments": [ "Reads sensors from a PMBus regulator rail" ], "id": "read_sensors_rule", "actions": [ { "comments": [ "Read output voltage from READ_VOUT." ], "pmbus_read_sensor": { "type": "vout", "command": "0x8B", "format": "linear_16" } } ] }, { "comments": [ "Detects presence of regulators associated with CPU3" ], "id": "detect_presence_rule", "actions": [ { "compare_presence": { "fru": "system/chassis/motherboard/cpu3", "value": true } } ] }, { "comments": [ "Detects and logs redundant phase faults" ], "id": "detect_phase_faults_rule", "actions": [ { "if": { "condition": { "i2c_compare_bit": { "register": "0x02", "position": 3, "value": 1 } }, "then": [ { "log_phase_fault": { "type": "n" } } ] } } ] } ], "chassis": [ { "comments": [ "Chassis number 1 containing CPUs and memory" ], "number": 1, "inventory_path": "system/chassis", "devices": [ { "comments": [ "IR35221 regulator producing the Vdd rail" ], "id": "vdd_regulator", "is_regulator": true, "fru": "system/chassis/motherboard/regulator1", "i2c_interface": { "bus": 1, "address": "0x70" }, "rails": [ { "comments": [ "Vdd rail" ], "id": "vdd", "configuration": { "volts": 1.03, "rule_id": "set_voltage_rule" }, "sensor_monitoring": { "rule_id": "read_sensors_rule" } } ] } ] } ] } )"_json; std::string getValidationToolCommand(const std::string& configFileName) { std::string command = "../phosphor-regulators/tools/validate-regulators-config.py -s \ ../phosphor-regulators/schema/config_schema.json -c "; command += configFileName; return command; } int runToolForOutputWithCommand(std::string command, std::string& standardOutput, std::string& standardError) { // run the validation tool with the temporary file and return the output // of the validation tool. TemporaryFile tmpFile; command += " 2> " + tmpFile.getPath().string(); // get the jsonschema print from validation tool. char buffer[256]; std::string result; // to get the stdout from the validation tool. FILE* pipe = popen(command.c_str(), "r"); if (!pipe) { throw std::runtime_error("popen() failed!"); } while (!std::feof(pipe)) { if (fgets(buffer, sizeof buffer, pipe) != NULL) { result += buffer; } } int returnValue = pclose(pipe); // Check if pclose() failed if (returnValue == -1) { // unable to close pipe. Print error and exit function. throw std::runtime_error("pclose() failed!"); } std::string firstLine = result.substr(0, result.find('\n')); standardOutput = firstLine; // Get command exit status from return value int exitStatus = WEXITSTATUS(returnValue); // Read the standardError from tmpFile. std::ifstream input(tmpFile.getPath()); std::string line; if (std::getline(input, line)) { standardError = line; } return exitStatus; } int runToolForOutput(const std::string& configFileName, std::string& output, std::string& error) { std::string command = getValidationToolCommand(configFileName); return runToolForOutputWithCommand(command, output, error); } void expectFileValid(const std::string& configFileName) { std::string errorMessage; std::string outputMessage; EXPECT_EQ(runToolForOutput(configFileName, outputMessage, errorMessage), 0); EXPECT_EQ(errorMessage, ""); EXPECT_EQ(outputMessage, ""); } void expectFileInvalid(const std::string& configFileName, const std::string& expectedErrorMessage, const std::string& expectedOutputMessage) { std::string errorMessage; std::string outputMessage; EXPECT_EQ(runToolForOutput(configFileName, outputMessage, errorMessage), 1); EXPECT_EQ(errorMessage, expectedErrorMessage); if (expectedOutputMessage != "") { EXPECT_EQ(outputMessage, expectedOutputMessage); } } void writeDataToFile(const json configFileJson, std::string fileName) { std::string jsonData = configFileJson.dump(); std::ofstream out(fileName); out << jsonData; out.close(); } void expectJsonValid(const json configFileJson) { TemporaryFile tmpFile; std::string fileName = tmpFile.getPath().string(); writeDataToFile(configFileJson, fileName); EXPECT_FILE_VALID(fileName); } void expectJsonInvalid(const json configFileJson, const std::string& expectedErrorMessage, const std::string& expectedOutputMessage) { TemporaryFile tmpFile; std::string fileName = tmpFile.getPath().string(); writeDataToFile(configFileJson, fileName); EXPECT_FILE_INVALID(fileName, expectedErrorMessage, expectedOutputMessage); } void expectCommandLineSyntax(const std::string& expectedErrorMessage, const std::string& expectedOutputMessage, const std::string& command, int status) { std::string errorMessage; std::string outputMessage; EXPECT_EQ(runToolForOutputWithCommand(command, outputMessage, errorMessage), status); EXPECT_EQ(errorMessage, expectedErrorMessage); EXPECT_EQ(outputMessage, expectedOutputMessage); } TEST(ValidateRegulatorsConfigTest, Action) { // Valid: Comments property not specified { json configFile = validConfigFile; EXPECT_JSON_VALID(configFile); } // Valid: Comments property specified { json configFile = validConfigFile; configFile["rules"][0]["actions"][0]["comments"][0] = "Set VOUT_COMMAND"; EXPECT_JSON_VALID(configFile); } // Valid: and action type specified { json configFile = validConfigFile; json andAction = R"( { "and": [ { "i2c_compare_byte": { "register": "0xA0", "value": "0x00" } }, { "i2c_compare_byte": { "register": "0xA1", "value": "0x00" } } ] } )"_json; configFile["rules"][0]["actions"].push_back(andAction); EXPECT_JSON_VALID(configFile); } // Valid: compare_presence action type specified { json configFile = validConfigFile; configFile["rules"][0]["actions"][1]["compare_presence"]["fru"] = "system/chassis/motherboard/regulator2"; configFile["rules"][0]["actions"][1]["compare_presence"]["value"] = true; EXPECT_JSON_VALID(configFile); } // Valid: compare_vpd action type specified { json configFile = validConfigFile; configFile["rules"][0]["actions"][1]["compare_vpd"]["fru"] = "system/chassis/motherboard/regulator2"; configFile["rules"][0]["actions"][1]["compare_vpd"]["keyword"] = "CCIN"; configFile["rules"][0]["actions"][1]["compare_vpd"]["value"] = "2D35"; EXPECT_JSON_VALID(configFile); } // Valid: i2c_capture_bytes action type specified { json configFile = validConfigFile; configFile["rules"][0]["actions"][1]["i2c_capture_bytes"]["register"] = "0xA0"; configFile["rules"][0]["actions"][1]["i2c_capture_bytes"]["count"] = 2; EXPECT_JSON_VALID(configFile); } // Valid: i2c_compare_bit action type specified { json configFile = validConfigFile; configFile["rules"][0]["actions"][1]["i2c_compare_bit"]["register"] = "0xA0"; configFile["rules"][0]["actions"][1]["i2c_compare_bit"]["position"] = 3; configFile["rules"][0]["actions"][1]["i2c_compare_bit"]["value"] = 1; EXPECT_JSON_VALID(configFile); } // Valid: i2c_compare_byte action type specified { json configFile = validConfigFile; configFile["rules"][0]["actions"][1]["i2c_compare_byte"]["register"] = "0x82"; configFile["rules"][0]["actions"][1]["i2c_compare_byte"]["value"] = "0x40"; configFile["rules"][0]["actions"][1]["i2c_compare_byte"]["mask"] = "0x7F"; EXPECT_JSON_VALID(configFile); } // Valid: i2c_compare_bytes action type specified { json configFile = validConfigFile; configFile["rules"][0]["actions"][1]["i2c_compare_bytes"]["register"] = "0x82"; configFile["rules"][0]["actions"][1]["i2c_compare_bytes"]["values"] = { "0x02", "0x73"}; configFile["rules"][0]["actions"][1]["i2c_compare_bytes"]["masks"] = { "0x7F", "0x7F"}; EXPECT_JSON_VALID(configFile); } // Valid: i2c_write_bit action type specified { json configFile = validConfigFile; configFile["rules"][0]["actions"][1]["i2c_write_bit"]["register"] = "0xA0"; configFile["rules"][0]["actions"][1]["i2c_write_bit"]["position"] = 3; configFile["rules"][0]["actions"][1]["i2c_write_bit"]["value"] = 1; EXPECT_JSON_VALID(configFile); } // Valid: i2c_write_byte action type specified { json configFile = validConfigFile; configFile["rules"][0]["actions"][1]["i2c_write_byte"]["register"] = "0x82"; configFile["rules"][0]["actions"][1]["i2c_write_byte"]["value"] = "0x40"; configFile["rules"][0]["actions"][1]["i2c_write_byte"]["mask"] = "0x7F"; EXPECT_JSON_VALID(configFile); } // Valid: i2c_write_bytes action type specified { json configFile = validConfigFile; configFile["rules"][0]["actions"][1]["i2c_write_bytes"]["register"] = "0x82"; configFile["rules"][0]["actions"][1]["i2c_write_bytes"]["values"] = { "0x02", "0x73"}; configFile["rules"][0]["actions"][1]["i2c_write_bytes"]["masks"] = { "0x7F", "0x7F"}; EXPECT_JSON_VALID(configFile); } // Valid: if action type specified { json configFile = validConfigFile; configFile["rules"][4]["actions"][0]["if"]["condition"]["run_rule"] = "set_voltage_rule"; configFile["rules"][4]["actions"][0]["if"]["then"][0]["run_rule"] = "read_sensors_rule"; configFile["rules"][4]["actions"][0]["if"]["else"][0]["run_rule"] = "read_sensors_rule"; configFile["rules"][4]["id"] = "rule_if"; EXPECT_JSON_VALID(configFile); } // Valid: log_phase_fault action type specified { json configFile = validConfigFile; configFile["rules"][0]["actions"][1]["log_phase_fault"]["type"] = "n+1"; EXPECT_JSON_VALID(configFile); } // Valid: not action type specified { json configFile = validConfigFile; configFile["rules"][0]["actions"][1]["not"]["i2c_compare_byte"] ["register"] = "0xA0"; configFile["rules"][0]["actions"][1]["not"]["i2c_compare_byte"] ["value"] = "0xFF"; EXPECT_JSON_VALID(configFile); } // Valid: or action type specified { json configFile = validConfigFile; configFile["rules"][0]["actions"][1]["or"][0]["i2c_compare_byte"] ["register"] = "0xA0"; configFile["rules"][0]["actions"][1]["or"][0]["i2c_compare_byte"] ["value"] = "0x00"; configFile["rules"][0]["actions"][1]["or"][1]["i2c_compare_byte"] ["register"] = "0xA1"; configFile["rules"][0]["actions"][1]["or"][1]["i2c_compare_byte"] ["value"] = "0x00"; EXPECT_JSON_VALID(configFile); } // Valid: pmbus_read_sensor and pmbus_write_vout_command action type // specified { EXPECT_JSON_VALID(validConfigFile); } // Valid: run_rule action type specified { json configFile = validConfigFile; configFile["rules"][0]["actions"][1]["run_rule"] = "read_sensors_rule"; EXPECT_JSON_VALID(configFile); } // Valid: set_device action type specified { json configFile = validConfigFile; configFile["rules"][0]["actions"][1]["set_device"] = "vdd_regulator"; EXPECT_JSON_VALID(configFile); } // Invalid: Wrong data type for comments (should be array of string) { json configFile = validConfigFile; configFile["rules"][0]["actions"][0]["comments"] = true; EXPECT_JSON_INVALID(configFile, "Validation failed.", "True is not of type 'array'"); } // Invalid: Wrong data type for action type (such as "i2c_write_byte": true) { json configFile = validConfigFile; configFile["rules"][0]["actions"][1]["i2c_write_byte"] = true; EXPECT_JSON_INVALID(configFile, "Validation failed.", "True is not of type 'object'"); } // Invalid: Empty comments array { json configFile = validConfigFile; configFile["rules"][0]["actions"][0]["comments"] = json::array(); EXPECT_JSON_INVALID(configFile, "Validation failed.", "[] is too short"); } // Invalid: Comments array has wrong element type (should be string) { json configFile = validConfigFile; configFile["rules"][0]["actions"][0]["comments"][0] = true; EXPECT_JSON_INVALID(configFile, "Validation failed.", "True is not of type 'string'"); } // Invalid: No action type specified { json configFile = validConfigFile; configFile["rules"][0]["actions"][1]["comments"][0] = "Check if bit 3 is on"; EXPECT_JSON_INVALID( configFile, "Validation failed.", "{'comments': ['Check if bit 3 is on']} is not valid under any of the given schemas"); } // Invalid: Multiple action types specified (such as both 'compare_presence' // and 'pmbus_write_vout_command') { json configFile = validConfigFile; configFile["rules"][0]["actions"][0]["compare_presence"]["value"] = true; EXPECT_JSON_INVALID( configFile, "Validation failed.", "{'compare_presence': {'value': True}, 'pmbus_write_vout_command': " "{'format': 'linear'}} is valid under each of {'required': " "['pmbus_write_vout_command']}, {'required': " "['compare_presence']}"); } // Invalid: Unexpected property specified (like 'foo') { json configFile = validConfigFile; configFile["rules"][0]["actions"][1]["foo"] = "foo"; EXPECT_JSON_INVALID( configFile, "Validation failed.", "Additional properties are not allowed ('foo' was unexpected)"); } } TEST(ValidateRegulatorsConfigTest, And) { // Valid. { json configFile = validConfigFile; json andAction = R"( { "and": [ { "i2c_compare_byte": { "register": "0xA0", "value": "0x00" } }, { "i2c_compare_byte": { "register": "0xA1", "value": "0x00" } } ] } )"_json; configFile["rules"][0]["actions"].push_back(andAction); EXPECT_JSON_VALID(configFile); } // Invalid: actions property value is an empty array. { json configFile = validConfigFile; json andAction = R"( { "and": [] } )"_json; configFile["rules"][0]["actions"].push_back(andAction); EXPECT_JSON_INVALID(configFile, "Validation failed.", "[] is too short"); } // Invalid: actions property has incorrect value data type. { json configFile = validConfigFile; json andAction = R"( { "and": true } )"_json; configFile["rules"][0]["actions"].push_back(andAction); EXPECT_JSON_INVALID(configFile, "Validation failed.", "True is not of type 'array'"); } // Invalid: actions property value contains wrong element type { json configFile = validConfigFile; json andAction = R"( { "and": ["foo"] } )"_json; configFile["rules"][0]["actions"].push_back(andAction); EXPECT_JSON_INVALID(configFile, "Validation failed.", "'foo' is not of type 'object'"); } } TEST(ValidateRegulatorsConfigTest, Chassis) { // Valid: test chassis. { json configFile = validConfigFile; EXPECT_JSON_VALID(configFile); } // Valid: test chassis with only required properties. { json configFile = validConfigFile; configFile["chassis"][0].erase("comments"); configFile["chassis"][0].erase("devices"); EXPECT_JSON_VALID(configFile); } // Invalid: test chassis with no number. { json configFile = validConfigFile; configFile["chassis"][0].erase("number"); EXPECT_JSON_INVALID(configFile, "Validation failed.", "'number' is a required property"); } // Invalid: test chassis with no inventory_path. { json configFile = validConfigFile; configFile["chassis"][0].erase("inventory_path"); EXPECT_JSON_INVALID(configFile, "Validation failed.", "'inventory_path' is a required property"); } // Invalid: test chassis with property comments wrong type. { json configFile = validConfigFile; configFile["chassis"][0]["comments"] = true; EXPECT_JSON_INVALID(configFile, "Validation failed.", "True is not of type 'array'"); } // Invalid: test chassis with property number wrong type. { json configFile = validConfigFile; configFile["chassis"][0]["number"] = 1.3; EXPECT_JSON_INVALID(configFile, "Validation failed.", "1.3 is not of type 'integer'"); } // Invalid: test chassis with property inventory_path wrong type. { json configFile = validConfigFile; configFile["chassis"][0]["inventory_path"] = 2; EXPECT_JSON_INVALID(configFile, "Validation failed.", "2 is not of type 'string'"); } // Invalid: test chassis with property devices wrong type. { json configFile = validConfigFile; configFile["chassis"][0]["devices"] = true; EXPECT_JSON_INVALID(configFile, "Validation failed.", "True is not of type 'array'"); } // Invalid: test chassis with property comments empty array. { json configFile = validConfigFile; configFile["chassis"][0]["comments"] = json::array(); EXPECT_JSON_INVALID(configFile, "Validation failed.", "[] is too short"); } // Invalid: test chassis with property devices empty array. { json configFile = validConfigFile; configFile["chassis"][0]["devices"] = json::array(); EXPECT_JSON_INVALID(configFile, "Validation failed.", "[] is too short"); } // Invalid: test chassis with property number less than 1. { json configFile = validConfigFile; configFile["chassis"][0]["number"] = 0; EXPECT_JSON_INVALID(configFile, "Validation failed.", "0 is less than the minimum of 1"); } // Invalid: test chassis with property inventory_path empty string. { json configFile = validConfigFile; configFile["chassis"][0]["inventory_path"] = ""; EXPECT_JSON_INVALID(configFile, "Validation failed.", "'' is too short"); } } TEST(ValidateRegulatorsConfigTest, ComparePresence) { json comparePresenceFile = validConfigFile; comparePresenceFile["rules"][0]["actions"][1]["compare_presence"]["fru"] = "system/chassis/motherboard/regulator2"; comparePresenceFile["rules"][0]["actions"][1]["compare_presence"]["value"] = true; // Valid. { json configFile = comparePresenceFile; EXPECT_JSON_VALID(configFile); } // Invalid: no FRU property. { json configFile = comparePresenceFile; configFile["rules"][0]["actions"][1]["compare_presence"].erase("fru"); EXPECT_JSON_INVALID(configFile, "Validation failed.", "'fru' is a required property"); } // Invalid: FRU property length is string less than 1. { json configFile = comparePresenceFile; configFile["rules"][0]["actions"][1]["compare_presence"]["fru"] = ""; EXPECT_JSON_INVALID(configFile, "Validation failed.", "'' is too short"); } // Invalid: no value property. { json configFile = comparePresenceFile; configFile["rules"][0]["actions"][1]["compare_presence"].erase("value"); EXPECT_JSON_INVALID(configFile, "Validation failed.", "'value' is a required property"); } // Invalid: value property type is not boolean. { json configFile = comparePresenceFile; configFile["rules"][0]["actions"][1]["compare_presence"]["value"] = "1"; EXPECT_JSON_INVALID(configFile, "Validation failed.", "'1' is not of type 'boolean'"); } // Invalid: FRU property type is not string. { json configFile = comparePresenceFile; configFile["rules"][0]["actions"][1]["compare_presence"]["fru"] = 1; EXPECT_JSON_INVALID(configFile, "Validation failed.", "1 is not of type 'string'"); } } TEST(ValidateRegulatorsConfigTest, CompareVpd) { json compareVpdFile = validConfigFile; compareVpdFile["rules"][0]["actions"][1]["compare_vpd"]["fru"] = "system/chassis/motherboard/regulator2"; compareVpdFile["rules"][0]["actions"][1]["compare_vpd"]["keyword"] = "CCIN"; compareVpdFile["rules"][0]["actions"][1]["compare_vpd"]["value"] = "2D35"; // Valid: value property: not empty. { json configFile = compareVpdFile; EXPECT_JSON_VALID(configFile); } // Valid: value property: empty. { json configFile = compareVpdFile; configFile["rules"][0]["actions"][1]["compare_vpd"]["value"] = ""; EXPECT_JSON_VALID(configFile); } // Valid: byte_values property: not empty. { json configFile = compareVpdFile; configFile["rules"][0]["actions"][1]["compare_vpd"].erase("value"); configFile["rules"][0]["actions"][1]["compare_vpd"]["byte_values"] = { "0x01", "0x02"}; EXPECT_JSON_VALID(configFile); } // Valid: byte_values property: empty. { json configFile = compareVpdFile; configFile["rules"][0]["actions"][1]["compare_vpd"].erase("value"); configFile["rules"][0]["actions"][1]["compare_vpd"]["byte_values"] = json::array(); EXPECT_JSON_VALID(configFile); } // Invalid: no FRU property. { json configFile = compareVpdFile; configFile["rules"][0]["actions"][1]["compare_vpd"].erase("fru"); EXPECT_JSON_INVALID(configFile, "Validation failed.", "'fru' is a required property"); } // Invalid: no keyword property. { json configFile = compareVpdFile; configFile["rules"][0]["actions"][1]["compare_vpd"].erase("keyword"); EXPECT_JSON_INVALID(configFile, "Validation failed.", "'keyword' is a required property"); } // Invalid: no value property. { json configFile = compareVpdFile; configFile["rules"][0]["actions"][1]["compare_vpd"].erase("value"); EXPECT_JSON_INVALID( configFile, "Validation failed.", "{'fru': 'system/chassis/motherboard/regulator2', 'keyword': 'CCIN'} is not valid under any of the given schemas"); } // Invalid: property FRU wrong type. { json configFile = compareVpdFile; configFile["rules"][0]["actions"][1]["compare_vpd"]["fru"] = 1; EXPECT_JSON_INVALID(configFile, "Validation failed.", "1 is not of type 'string'"); } // Invalid: property FRU is string less than 1. { json configFile = compareVpdFile; configFile["rules"][0]["actions"][1]["compare_vpd"]["fru"] = ""; EXPECT_JSON_INVALID(configFile, "Validation failed.", "'' is too short"); } // Invalid: property keyword is not "CCIN", "Manufacturer", "Model", // "PartNumber", "HW" { json configFile = compareVpdFile; configFile["rules"][0]["actions"][1]["compare_vpd"]["keyword"] = "Number"; EXPECT_JSON_INVALID(configFile, "Validation failed.", "'Number' is not one of ['CCIN', " "'Manufacturer', 'Model', 'PartNumber', 'HW']"); } // Invalid: property value wrong type. { json configFile = compareVpdFile; configFile["rules"][0]["actions"][1]["compare_vpd"]["value"] = 1; EXPECT_JSON_INVALID(configFile, "Validation failed.", "1 is not of type 'string'"); } // Invalid: property byte_values has wrong type { json configFile = compareVpdFile; configFile["rules"][0]["actions"][1]["compare_vpd"].erase("value"); configFile["rules"][0]["actions"][1]["compare_vpd"]["byte_values"] = "0x50"; EXPECT_JSON_INVALID(configFile, "Validation failed.", "'0x50' is not of type 'array'"); } // Invalid: properties byte_values and value both exist { json configFile = compareVpdFile; configFile["rules"][0]["actions"][1]["compare_vpd"]["byte_values"] = { "0x01", "0x02"}; EXPECT_JSON_INVALID( configFile, "Validation failed.", "{'byte_values': ['0x01', '0x02'], 'fru': " "'system/chassis/motherboard/regulator2', 'keyword': 'CCIN', " "'value': '2D35'} is valid under each of {'required': " "['byte_values']}, {'required': ['value']}"); } } TEST(ValidateRegulatorsConfigTest, ConfigFile) { // Valid: Only required properties specified { json configFile; configFile["chassis"][0]["number"] = 1; configFile["chassis"][0]["inventory_path"] = "system/chassis"; EXPECT_JSON_VALID(configFile); } // Valid: All properties specified { json configFile = validConfigFile; EXPECT_JSON_VALID(configFile); } // Invalid: Required chassis property not specified { json configFile = validConfigFile; configFile.erase("chassis"); EXPECT_JSON_INVALID(configFile, "Validation failed.", "'chassis' is a required property"); } // Invalid: Wrong data type for comments { json configFile = validConfigFile; configFile["comments"] = true; EXPECT_JSON_INVALID(configFile, "Validation failed.", "True is not of type 'array'"); } // Invalid: Wrong data type for rules { json configFile = validConfigFile; configFile["rules"] = true; EXPECT_JSON_INVALID(configFile, "Validation failed.", "True is not of type 'array'"); } // Invalid: Wrong data type for chassis { json configFile = validConfigFile; configFile["chassis"] = true; EXPECT_JSON_INVALID(configFile, "Validation failed.", "True is not of type 'array'"); } // Invalid: Empty comments array; { json configFile = validConfigFile; configFile["comments"] = json::array(); EXPECT_JSON_INVALID(configFile, "Validation failed.", "[] is too short"); } // Invalid: Empty rules array { json configFile = validConfigFile; configFile["rules"] = json::array(); EXPECT_JSON_INVALID(configFile, "Validation failed.", "[] is too short"); } // Invalid: Empty chassis array { json configFile = validConfigFile; configFile["chassis"] = json::array(); EXPECT_JSON_INVALID(configFile, "Validation failed.", "[] is too short"); } // Invalid: Comments array has wrong element type (should be string) { json configFile = validConfigFile; configFile["comments"][0] = true; EXPECT_JSON_INVALID(configFile, "Validation failed.", "True is not of type 'string'"); } // Invalid: Rules array has wrong element type (should be rule) { json configFile = validConfigFile; configFile["rules"][0] = true; EXPECT_JSON_INVALID(configFile, "Validation failed.", "True is not of type 'object'"); } // Invalid: Chassis array has wrong element type (should be chassis) { json configFile = validConfigFile; configFile["chassis"][0] = true; EXPECT_JSON_INVALID(configFile, "Validation failed.", "True is not of type 'object'"); } // Invalid: Unexpected property specified { json configFile = validConfigFile; configFile["foo"] = json::array(); EXPECT_JSON_INVALID( configFile, "Validation failed.", "Additional properties are not allowed ('foo' was unexpected)"); } } TEST(ValidateRegulatorsConfigTest, Configuration) { json configurationFile = validConfigFile; configurationFile["chassis"][0]["devices"][0]["configuration"]["comments"] [0] = "Set rail to 1.25V using standard rule"; configurationFile["chassis"][0]["devices"][0]["configuration"]["volts"] = 1.25; configurationFile["chassis"][0]["devices"][0]["configuration"]["rule_id"] = "set_voltage_rule"; // Valid: test configuration with property rule_id and with no actions. { json configFile = configurationFile; configFile["chassis"][0]["devices"][0]["configuration"]["comments"][1] = "test multiple array elements in comments."; EXPECT_JSON_VALID(configFile); } // Valid: test configuration with property actions and with no rule_id. { json configFile = configurationFile; configFile["chassis"][0]["devices"][0]["configuration"].erase( "rule_id"); configFile["chassis"][0]["devices"][0]["configuration"]["actions"][0] ["compare_presence"]["fru"] = "system/chassis/motherboard/cpu3"; configFile["chassis"][0]["devices"][0]["configuration"]["actions"][0] ["compare_presence"]["value"] = true; EXPECT_JSON_VALID(configFile); } // Valid: comments not specified (optional property). { json configFile = configurationFile; configFile["chassis"][0]["devices"][0]["configuration"].erase( "comments"); EXPECT_JSON_VALID(configFile); } // Valid: volts not specified (optional property). { json configFile = configurationFile; configFile["chassis"][0]["devices"][0]["configuration"].erase("volts"); EXPECT_JSON_VALID(configFile); } // Valid: configuration is property of a rail (vs. a device). { json configFile = validConfigFile; configFile["chassis"][0]["devices"][0]["rails"][0]["configuration"] ["comments"][0] = "Set rail to 1.25V using standard rule"; configFile["chassis"][0]["devices"][0]["rails"][0]["configuration"] ["volts"] = 1.25; configFile["chassis"][0]["devices"][0]["rails"][0]["configuration"] ["rule_id"] = "set_voltage_rule"; EXPECT_JSON_VALID(configFile); } // Invalid: comments property has wrong data type (not an array). { json configFile = configurationFile; configFile["chassis"][0]["devices"][0]["configuration"]["comments"] = 1; EXPECT_JSON_INVALID(configFile, "Validation failed.", "1 is not of type 'array'"); } // Invalid: test configuration with both actions and rule_id properties. { json configFile = configurationFile; configFile["chassis"][0]["devices"][0]["configuration"]["actions"][0] ["compare_presence"]["fru"] = "system/chassis/motherboard/cpu3"; configFile["chassis"][0]["devices"][0]["configuration"]["actions"][0] ["compare_presence"]["value"] = true; EXPECT_JSON_INVALID(configFile, "Validation failed.", ""); } // Invalid: test configuration with no rule_id and actions. { json configFile = configurationFile; configFile["chassis"][0]["devices"][0]["configuration"].erase( "rule_id"); EXPECT_JSON_INVALID( configFile, "Validation failed.", "{'comments': ['Set rail to 1.25V using standard rule'], 'volts': 1.25} is not valid under any of the given schemas"); } // Invalid: test configuration with property volts wrong type. { json configFile = configurationFile; configFile["chassis"][0]["devices"][0]["configuration"]["volts"] = true; EXPECT_JSON_INVALID(configFile, "Validation failed.", "True is not of type 'number'"); } // Invalid: test configuration with property rule_id wrong type. { json configFile = configurationFile; configFile["chassis"][0]["devices"][0]["configuration"]["rule_id"] = true; EXPECT_JSON_INVALID(configFile, "Validation failed.", "True is not of type 'string'"); } // Invalid: test configuration with property actions wrong type. { json configFile = configurationFile; configFile["chassis"][0]["devices"][0]["configuration"].erase( "rule_id"); configFile["chassis"][0]["devices"][0]["configuration"]["actions"] = true; EXPECT_JSON_INVALID(configFile, "Validation failed.", "True is not of type 'array'"); } // Invalid: test configuration with property comments empty array. { json configFile = configurationFile; configFile["chassis"][0]["devices"][0]["configuration"]["comments"] = json::array(); EXPECT_JSON_INVALID(configFile, "Validation failed.", "[] is too short"); } // Invalid: test configuration with property rule_id wrong format. { json configFile = configurationFile; configFile["chassis"][0]["devices"][0]["configuration"]["rule_id"] = "id!"; EXPECT_JSON_INVALID(configFile, "Validation failed.", "'id!' does not match '^[A-Za-z0-9_]+$'"); } // Invalid: test configuration with property actions empty array. { json configFile = configurationFile; configFile["chassis"][0]["devices"][0]["configuration"].erase( "rule_id"); configFile["chassis"][0]["devices"][0]["configuration"]["actions"] = json::array(); EXPECT_JSON_INVALID(configFile, "Validation failed.", "[] is too short"); } } TEST(ValidateRegulatorsConfigTest, Device) { // Valid: test devices. { json configFile = validConfigFile; EXPECT_JSON_VALID(configFile); } // Valid: test devices with required properties. { json configFile = validConfigFile; configFile["chassis"][0]["devices"][0].erase("comments"); configFile["chassis"][0]["devices"][0].erase("presence_detection"); configFile["chassis"][0]["devices"][0].erase("configuration"); configFile["chassis"][0]["devices"][0].erase("phase_fault_detection"); configFile["chassis"][0]["devices"][0].erase("rails"); EXPECT_JSON_VALID(configFile); } // Invalid: test devices with no id. { json configFile = validConfigFile; configFile["chassis"][0]["devices"][0].erase("id"); EXPECT_JSON_INVALID(configFile, "Validation failed.", "'id' is a required property"); } // Invalid: test devices with no is_regulator. { json configFile = validConfigFile; configFile["chassis"][0]["devices"][0].erase("is_regulator"); EXPECT_JSON_INVALID( configFile, "Validation failed.", "{'comments': ['IR35221 regulator producing the Vdd rail'], 'fru': 'system/chassis/motherboard/regulator1', 'i2c_interface': {'address': '0x70', 'bus': 1}, 'id': 'vdd_regulator', 'rails': [{'comments': ['Vdd rail'], 'configuration': {'rule_id': 'set_voltage_rule', 'volts': 1.03}, 'id': 'vdd', 'sensor_monitoring': {'rule_id': 'read_sensors_rule'}}]} should not be valid under {'anyOf': [{'required': ['phase_fault_detection']}, {'required': ['rails']}]}"); } // Invalid: test devices with no fru. { json configFile = validConfigFile; configFile["chassis"][0]["devices"][0].erase("fru"); EXPECT_JSON_INVALID(configFile, "Validation failed.", "'fru' is a required property"); } // Invalid: test devices with no i2c_interface. { json configFile = validConfigFile; configFile["chassis"][0]["devices"][0].erase("i2c_interface"); EXPECT_JSON_INVALID(configFile, "Validation failed.", "'i2c_interface' is a required property"); } // Invalid: is_regulator=false: phase_fault_detection specified { json configFile = validConfigFile; configFile["chassis"][0]["devices"][0]["is_regulator"] = false; configFile["chassis"][0]["devices"][0].erase("rails"); configFile["chassis"][0]["devices"][0]["phase_fault_detection"] ["rule_id"] = "detect_phase_faults_rule"; EXPECT_JSON_INVALID(configFile, "Validation failed.", ""); } // Invalid: is_regulator=false: rails specified { json configFile = validConfigFile; configFile["chassis"][0]["devices"][0]["is_regulator"] = false; EXPECT_JSON_INVALID(configFile, "Validation failed.", ""); } // Invalid: is_regulator=false: phase_fault_detection and rails specified { json configFile = validConfigFile; configFile["chassis"][0]["devices"][0]["is_regulator"] = false; configFile["chassis"][0]["devices"][0]["phase_fault_detection"] ["rule_id"] = "detect_phase_faults_rule"; EXPECT_JSON_INVALID(configFile, "Validation failed.", ""); } // Invalid: test devices with property comments wrong type. { json configFile = validConfigFile; configFile["chassis"][0]["devices"][0]["comments"] = true; EXPECT_JSON_INVALID(configFile, "Validation failed.", "True is not of type 'array'"); } // Invalid: test devices with property id wrong type. { json configFile = validConfigFile; configFile["chassis"][0]["devices"][0]["id"] = true; EXPECT_JSON_INVALID(configFile, "Validation failed.", "True is not of type 'string'"); } // Invalid: test devices with property is_regulator wrong type. { json configFile = validConfigFile; configFile["chassis"][0]["devices"][0]["is_regulator"] = 1; EXPECT_JSON_INVALID(configFile, "Validation failed.", "1 is not of type 'boolean'"); } // Invalid: test devices with property fru wrong type. { json configFile = validConfigFile; configFile["chassis"][0]["devices"][0]["fru"] = true; EXPECT_JSON_INVALID(configFile, "Validation failed.", "True is not of type 'string'"); } // Invalid: test devices with property i2c_interface wrong type. { json configFile = validConfigFile; configFile["chassis"][0]["devices"][0]["i2c_interface"] = true; EXPECT_JSON_INVALID(configFile, "Validation failed.", "True is not of type 'object'"); } // Invalid: test devices with property presence_detection wrong // type. { json configFile = validConfigFile; configFile["chassis"][0]["devices"][0]["presence_detection"] = true; EXPECT_JSON_INVALID(configFile, "Validation failed.", "True is not of type 'object'"); } // Invalid: test devices with property configuration wrong type. { json configFile = validConfigFile; configFile["chassis"][0]["devices"][0]["configuration"] = true; EXPECT_JSON_INVALID(configFile, "Validation failed.", "True is not of type 'object'"); } // Invalid: test devices with property phase_fault_detection wrong type. { json configFile = validConfigFile; configFile["chassis"][0]["devices"][0]["phase_fault_detection"] = true; EXPECT_JSON_INVALID(configFile, "Validation failed.", "True is not of type 'object'"); } // Invalid: test devices with property rails wrong type. { json configFile = validConfigFile; configFile["chassis"][0]["devices"][0]["rails"] = true; EXPECT_JSON_INVALID(configFile, "Validation failed.", "True is not of type 'array'"); } // Invalid: test devices with property comments empty array. { json configFile = validConfigFile; configFile["chassis"][0]["devices"][0]["comments"] = json::array(); EXPECT_JSON_INVALID(configFile, "Validation failed.", "[] is too short"); } // Invalid: test devices with property fru length less than 1. { json configFile = validConfigFile; configFile["chassis"][0]["devices"][0]["fru"] = ""; EXPECT_JSON_INVALID(configFile, "Validation failed.", "'' is too short"); } // Invalid: test devices with property id wrong format. { json configFile = validConfigFile; configFile["chassis"][0]["devices"][0]["id"] = "id#"; EXPECT_JSON_INVALID(configFile, "Validation failed.", "'id#' does not match '^[A-Za-z0-9_]+$'"); } // Invalid: test devices with property rails empty array. { json configFile = validConfigFile; configFile["chassis"][0]["devices"][0]["rails"] = json::array(); EXPECT_JSON_INVALID(configFile, "Validation failed.", "[] is too short"); } } TEST(ValidateRegulatorsConfigTest, I2CCaptureBytes) { json initialFile = validConfigFile; initialFile["rules"][0]["actions"][1]["i2c_capture_bytes"]["register"] = "0xA0"; initialFile["rules"][0]["actions"][1]["i2c_capture_bytes"]["count"] = 2; // Valid: All required properties { json configFile = initialFile; EXPECT_JSON_VALID(configFile); } // Invalid: register not specified { json configFile = initialFile; configFile["rules"][0]["actions"][1]["i2c_capture_bytes"].erase( "register"); EXPECT_JSON_INVALID(configFile, "Validation failed.", "'register' is a required property"); } // Invalid: count not specified { json configFile = initialFile; configFile["rules"][0]["actions"][1]["i2c_capture_bytes"].erase( "count"); EXPECT_JSON_INVALID(configFile, "Validation failed.", "'count' is a required property"); } // Invalid: invalid property specified { json configFile = initialFile; configFile["rules"][0]["actions"][1]["i2c_capture_bytes"]["foo"] = true; EXPECT_JSON_INVALID( configFile, "Validation failed.", "Additional properties are not allowed ('foo' was unexpected)"); } // Invalid: register has wrong data type { json configFile = initialFile; configFile["rules"][0]["actions"][1]["i2c_capture_bytes"]["register"] = 1; EXPECT_JSON_INVALID(configFile, "Validation failed.", "1 is not of type 'string'"); } // Invalid: register has wrong format { json configFile = initialFile; configFile["rules"][0]["actions"][1]["i2c_capture_bytes"]["register"] = "0xA00"; EXPECT_JSON_INVALID(configFile, "Validation failed.", "'0xA00' does not match '^0x[0-9A-Fa-f]{2}$'"); } // Invalid: count has wrong data type { json configFile = initialFile; configFile["rules"][0]["actions"][1]["i2c_capture_bytes"]["count"] = 3.1; EXPECT_JSON_INVALID(configFile, "Validation failed.", "3.1 is not of type 'integer'"); } // Invalid: count < 1 { json configFile = initialFile; configFile["rules"][0]["actions"][1]["i2c_capture_bytes"]["count"] = 0; EXPECT_JSON_INVALID(configFile, "Validation failed.", "0 is less than the minimum of 1"); } } TEST(ValidateRegulatorsConfigTest, I2CCompareBit) { json i2cCompareBitFile = validConfigFile; i2cCompareBitFile["rules"][0]["actions"][1]["i2c_compare_bit"]["register"] = "0xA0"; i2cCompareBitFile["rules"][0]["actions"][1]["i2c_compare_bit"]["position"] = 3; i2cCompareBitFile["rules"][0]["actions"][1]["i2c_compare_bit"]["value"] = 1; // Valid: test rule actions i2c_compare_bit. { json configFile = i2cCompareBitFile; EXPECT_JSON_VALID(configFile); } // Invalid: test i2c_compare_bit with no register. { json configFile = i2cCompareBitFile; configFile["rules"][0]["actions"][1]["i2c_compare_bit"].erase( "register"); EXPECT_JSON_INVALID(configFile, "Validation failed.", "'register' is a required property"); } // Invalid: test i2c_compare_bit with no position. { json configFile = i2cCompareBitFile; configFile["rules"][0]["actions"][1]["i2c_compare_bit"].erase( "position"); EXPECT_JSON_INVALID(configFile, "Validation failed.", "'position' is a required property"); } // Invalid: test i2c_compare_bit with no value. { json configFile = i2cCompareBitFile; configFile["rules"][0]["actions"][1]["i2c_compare_bit"].erase("value"); EXPECT_JSON_INVALID(configFile, "Validation failed.", "'value' is a required property"); } // Invalid: test i2c_compare_bit with register wrong type. { json configFile = i2cCompareBitFile; configFile["rules"][0]["actions"][1]["i2c_compare_bit"]["register"] = 1; EXPECT_JSON_INVALID(configFile, "Validation failed.", "1 is not of type 'string'"); } // Invalid: test i2c_compare_bit with register wrong format. { json configFile = i2cCompareBitFile; configFile["rules"][0]["actions"][1]["i2c_compare_bit"]["register"] = "0xA00"; EXPECT_JSON_INVALID(configFile, "Validation failed.", "'0xA00' does not match '^0x[0-9A-Fa-f]{2}$'"); } // Invalid: test i2c_compare_bit with position wrong type. { json configFile = i2cCompareBitFile; configFile["rules"][0]["actions"][1]["i2c_compare_bit"]["position"] = 3.1; EXPECT_JSON_INVALID(configFile, "Validation failed.", "3.1 is not of type 'integer'"); } // Invalid: test i2c_compare_bit with position greater than 7. { json configFile = i2cCompareBitFile; configFile["rules"][0]["actions"][1]["i2c_compare_bit"]["position"] = 8; EXPECT_JSON_INVALID(configFile, "Validation failed.", "8 is greater than the maximum of 7"); } // Invalid: test i2c_compare_bit with position less than 0. { json configFile = i2cCompareBitFile; configFile["rules"][0]["actions"][1]["i2c_compare_bit"]["position"] = -1; EXPECT_JSON_INVALID(configFile, "Validation failed.", "-1 is less than the minimum of 0"); } // Invalid: test i2c_compare_bit with value wrong type. { json configFile = i2cCompareBitFile; configFile["rules"][0]["actions"][1]["i2c_compare_bit"]["value"] = "1"; EXPECT_JSON_INVALID(configFile, "Validation failed.", "'1' is not of type 'integer'"); } // Invalid: test i2c_compare_bit with value greater than 1. { json configFile = i2cCompareBitFile; configFile["rules"][0]["actions"][1]["i2c_compare_bit"]["value"] = 2; EXPECT_JSON_INVALID(configFile, "Validation failed.", "2 is greater than the maximum of 1"); } // Invalid: test i2c_compare_bit with value less than 0. { json configFile = i2cCompareBitFile; configFile["rules"][0]["actions"][1]["i2c_compare_bit"]["value"] = -1; EXPECT_JSON_INVALID(configFile, "Validation failed.", "-1 is less than the minimum of 0"); } } TEST(ValidateRegulatorsConfigTest, I2CCompareByte) { json i2cCompareByteFile = validConfigFile; i2cCompareByteFile["rules"][0]["actions"][1]["i2c_compare_byte"] ["register"] = "0x82"; i2cCompareByteFile["rules"][0]["actions"][1]["i2c_compare_byte"]["value"] = "0x40"; i2cCompareByteFile["rules"][0]["actions"][1]["i2c_compare_byte"]["mask"] = "0x7F"; // Valid: test i2c_compare_byte with all properties. { json configFile = i2cCompareByteFile; EXPECT_JSON_VALID(configFile); } // Valid: test i2c_compare_byte with all required properties. { json configFile = i2cCompareByteFile; configFile["rules"][0]["actions"][1]["i2c_compare_byte"].erase("mask"); EXPECT_JSON_VALID(configFile); } // Invalid: test i2c_compare_byte with no register. { json configFile = i2cCompareByteFile; configFile["rules"][0]["actions"][1]["i2c_compare_byte"].erase( "register"); EXPECT_JSON_INVALID(configFile, "Validation failed.", "'register' is a required property"); } // Invalid: test i2c_compare_byte with no value. { json configFile = i2cCompareByteFile; configFile["rules"][0]["actions"][1]["i2c_compare_byte"].erase("value"); EXPECT_JSON_INVALID(configFile, "Validation failed.", "'value' is a required property"); } // Invalid: test i2c_compare_byte with property register wrong type. { json configFile = i2cCompareByteFile; configFile["rules"][0]["actions"][1]["i2c_compare_byte"]["register"] = 1; EXPECT_JSON_INVALID(configFile, "Validation failed.", "1 is not of type 'string'"); } // Invalid: test i2c_compare_byte with property value wrong type. { json configFile = i2cCompareByteFile; configFile["rules"][0]["actions"][1]["i2c_compare_byte"]["value"] = 1; EXPECT_JSON_INVALID(configFile, "Validation failed.", "1 is not of type 'string'"); } // Invalid: test i2c_compare_byte with property mask wrong type. { json configFile = i2cCompareByteFile; configFile["rules"][0]["actions"][1]["i2c_compare_byte"]["mask"] = 1; EXPECT_JSON_INVALID(configFile, "Validation failed.", "1 is not of type 'string'"); } // Invalid: test i2c_compare_byte with property register more than 2 hex // digits. { json configFile = i2cCompareByteFile; configFile["rules"][0]["actions"][1]["i2c_compare_byte"]["register"] = "0x820"; EXPECT_JSON_INVALID(configFile, "Validation failed.", "'0x820' does not match '^0x[0-9A-Fa-f]{2}$'"); } // Invalid: test i2c_compare_byte with property value more than 2 hex // digits. { json configFile = i2cCompareByteFile; configFile["rules"][0]["actions"][1]["i2c_compare_byte"]["value"] = "0x820"; EXPECT_JSON_INVALID(configFile, "Validation failed.", "'0x820' does not match '^0x[0-9A-Fa-f]{2}$'"); } // Invalid: test i2c_compare_byte with property mask more than 2 hex digits. { json configFile = i2cCompareByteFile; configFile["rules"][0]["actions"][1]["i2c_compare_byte"]["mask"] = "0x820"; EXPECT_JSON_INVALID(configFile, "Validation failed.", "'0x820' does not match '^0x[0-9A-Fa-f]{2}$'"); } // Invalid: test i2c_compare_byte with property register less than 2 hex // digits. { json configFile = i2cCompareByteFile; configFile["rules"][0]["actions"][1]["i2c_compare_byte"]["register"] = "0x8"; EXPECT_JSON_INVALID(configFile, "Validation failed.", "'0x8' does not match '^0x[0-9A-Fa-f]{2}$'"); } // Invalid: test i2c_compare_byte with property value less than 2 hex // digits. { json configFile = i2cCompareByteFile; configFile["rules"][0]["actions"][1]["i2c_compare_byte"]["value"] = "0x8"; EXPECT_JSON_INVALID(configFile, "Validation failed.", "'0x8' does not match '^0x[0-9A-Fa-f]{2}$'"); } // Invalid: test i2c_compare_byte with property mask less than 2 hex digits. { json configFile = i2cCompareByteFile; configFile["rules"][0]["actions"][1]["i2c_compare_byte"]["mask"] = "0x8"; EXPECT_JSON_INVALID(configFile, "Validation failed.", "'0x8' does not match '^0x[0-9A-Fa-f]{2}$'"); } // Invalid: test i2c_compare_byte with property register no leading prefix. { json configFile = i2cCompareByteFile; configFile["rules"][0]["actions"][1]["i2c_compare_byte"]["register"] = "82"; EXPECT_JSON_INVALID(configFile, "Validation failed.", "'82' does not match '^0x[0-9A-Fa-f]{2}$'"); } // Invalid: test i2c_compare_byte with property value no leading prefix. { json configFile = i2cCompareByteFile; configFile["rules"][0]["actions"][1]["i2c_compare_byte"]["value"] = "82"; EXPECT_JSON_INVALID(configFile, "Validation failed.", "'82' does not match '^0x[0-9A-Fa-f]{2}$'"); } // Invalid: test i2c_compare_byte with property mask no leading prefix. { json configFile = i2cCompareByteFile; configFile["rules"][0]["actions"][1]["i2c_compare_byte"]["mask"] = "82"; EXPECT_JSON_INVALID(configFile, "Validation failed.", "'82' does not match '^0x[0-9A-Fa-f]{2}$'"); } // Invalid: test i2c_compare_byte with property register invalid hex digit. { json configFile = i2cCompareByteFile; configFile["rules"][0]["actions"][1]["i2c_compare_byte"]["register"] = "0xG1"; EXPECT_JSON_INVALID(configFile, "Validation failed.", "'0xG1' does not match '^0x[0-9A-Fa-f]{2}$'"); } // Invalid: test i2c_compare_byte with property value invalid hex digit. { json configFile = i2cCompareByteFile; configFile["rules"][0]["actions"][1]["i2c_compare_byte"]["value"] = "0xG1"; EXPECT_JSON_INVALID(configFile, "Validation failed.", "'0xG1' does not match '^0x[0-9A-Fa-f]{2}$'"); } // Invalid: test i2c_compare_byte with property mask invalid hex digit. { json configFile = i2cCompareByteFile; configFile["rules"][0]["actions"][1]["i2c_compare_byte"]["mask"] = "0xG1"; EXPECT_JSON_INVALID(configFile, "Validation failed.", "'0xG1' does not match '^0x[0-9A-Fa-f]{2}$'"); } } TEST(ValidateRegulatorsConfigTest, I2CCompareBytes) { json i2cCompareBytesFile = validConfigFile; i2cCompareBytesFile["rules"][0]["actions"][1]["i2c_compare_bytes"] ["register"] = "0x82"; i2cCompareBytesFile["rules"][0]["actions"][1]["i2c_compare_bytes"] ["values"] = {"0x02", "0x73"}; i2cCompareBytesFile["rules"][0]["actions"][1]["i2c_compare_bytes"] ["masks"] = {"0x7F", "0x7F"}; // Valid: test i2c_compare_bytes. { json configFile = i2cCompareBytesFile; EXPECT_JSON_VALID(configFile); } // Valid: test i2c_compare_bytes with all required properties. { json configFile = i2cCompareBytesFile; configFile["rules"][0]["actions"][1]["i2c_compare_bytes"].erase( "masks"); EXPECT_JSON_VALID(configFile); } // Invalid: test i2c_compare_bytes with no register. { json configFile = i2cCompareBytesFile; configFile["rules"][0]["actions"][1]["i2c_compare_bytes"].erase( "register"); EXPECT_JSON_INVALID(configFile, "Validation failed.", "'register' is a required property"); } // Invalid: test i2c_compare_bytes with no values. { json configFile = i2cCompareBytesFile; configFile["rules"][0]["actions"][1]["i2c_compare_bytes"].erase( "values"); EXPECT_JSON_INVALID(configFile, "Validation failed.", "'values' is a required property"); } // Invalid: test i2c_compare_bytes with property values as empty array. { json configFile = i2cCompareBytesFile; configFile["rules"][0]["actions"][1]["i2c_compare_bytes"]["values"] = json::array(); EXPECT_JSON_INVALID(configFile, "Validation failed.", "[] is too short"); } // Invalid: test i2c_compare_bytes with property masks as empty array. { json configFile = i2cCompareBytesFile; configFile["rules"][0]["actions"][1]["i2c_compare_bytes"]["masks"] = json::array(); EXPECT_JSON_INVALID(configFile, "Validation failed.", "[] is too short"); } // Invalid: test i2c_compare_bytes with property register wrong type. { json configFile = i2cCompareBytesFile; configFile["rules"][0]["actions"][1]["i2c_compare_bytes"]["register"] = 1; EXPECT_JSON_INVALID(configFile, "Validation failed.", "1 is not of type 'string'"); } // Invalid: test i2c_compare_bytes with property values wrong type. { json configFile = i2cCompareBytesFile; configFile["rules"][0]["actions"][1]["i2c_compare_bytes"]["values"] = 1; EXPECT_JSON_INVALID(configFile, "Validation failed.", "1 is not of type 'array'"); } // Invalid: test i2c_compare_bytes with property masks wrong type. { json configFile = i2cCompareBytesFile; configFile["rules"][0]["actions"][1]["i2c_compare_bytes"]["masks"] = 1; EXPECT_JSON_INVALID(configFile, "Validation failed.", "1 is not of type 'array'"); } // Invalid: test i2c_compare_bytes with property register more than 2 hex // digits. { json configFile = i2cCompareBytesFile; configFile["rules"][0]["actions"][1]["i2c_compare_bytes"]["register"] = "0x820"; EXPECT_JSON_INVALID(configFile, "Validation failed.", "'0x820' does not match '^0x[0-9A-Fa-f]{2}$'"); } // Invalid: test i2c_compare_bytes with property values more than 2 hex // digits. { json configFile = i2cCompareBytesFile; configFile["rules"][0]["actions"][1]["i2c_compare_bytes"]["values"][0] = "0x820"; EXPECT_JSON_INVALID(configFile, "Validation failed.", "'0x820' does not match '^0x[0-9A-Fa-f]{2}$'"); } // Invalid: test i2c_compare_bytes with property masks more than 2 hex // digits. { json configFile = i2cCompareBytesFile; configFile["rules"][0]["actions"][1]["i2c_compare_bytes"]["masks"][0] = "0x820"; EXPECT_JSON_INVALID(configFile, "Validation failed.", "'0x820' does not match '^0x[0-9A-Fa-f]{2}$'"); } // Invalid: test i2c_compare_bytes with property register less than 2 hex // digits. { json configFile = i2cCompareBytesFile; configFile["rules"][0]["actions"][1]["i2c_compare_bytes"]["register"] = "0x8"; EXPECT_JSON_INVALID(configFile, "Validation failed.", "'0x8' does not match '^0x[0-9A-Fa-f]{2}$'"); } // Invalid: test i2c_compare_bytes with property values less than 2 hex // digits. { json configFile = i2cCompareBytesFile; configFile["rules"][0]["actions"][1]["i2c_compare_bytes"]["values"][0] = "0x8"; EXPECT_JSON_INVALID(configFile, "Validation failed.", "'0x8' does not match '^0x[0-9A-Fa-f]{2}$'"); } // Invalid: test i2c_compare_bytes with property masks less than 2 hex // digits. { json configFile = i2cCompareBytesFile; configFile["rules"][0]["actions"][1]["i2c_compare_bytes"]["masks"][0] = "0x8"; EXPECT_JSON_INVALID(configFile, "Validation failed.", "'0x8' does not match '^0x[0-9A-Fa-f]{2}$'"); } // Invalid: test i2c_compare_bytes with property register no leading prefix. { json configFile = i2cCompareBytesFile; configFile["rules"][0]["actions"][1]["i2c_compare_bytes"]["register"] = "82"; EXPECT_JSON_INVALID(configFile, "Validation failed.", "'82' does not match '^0x[0-9A-Fa-f]{2}$'"); } // Invalid: test i2c_compare_bytes with property values no leading prefix. { json configFile = i2cCompareBytesFile; configFile["rules"][0]["actions"][1]["i2c_compare_bytes"]["values"][0] = "82"; EXPECT_JSON_INVALID(configFile, "Validation failed.", "'82' does not match '^0x[0-9A-Fa-f]{2}$'"); } // Invalid: test i2c_compare_bytes with property masks no leading prefix. { json configFile = i2cCompareBytesFile; configFile["rules"][0]["actions"][1]["i2c_compare_bytes"]["masks"][0] = "82"; EXPECT_JSON_INVALID(configFile, "Validation failed.", "'82' does not match '^0x[0-9A-Fa-f]{2}$'"); } // Invalid: test i2c_compare_bytes with property register invalid hex digit. { json configFile = i2cCompareBytesFile; configFile["rules"][0]["actions"][1]["i2c_compare_bytes"]["register"] = "0xG1"; EXPECT_JSON_INVALID(configFile, "Validation failed.", "'0xG1' does not match '^0x[0-9A-Fa-f]{2}$'"); } // Invalid: test i2c_compare_bytes with property values invalid hex digit. { json configFile = i2cCompareBytesFile; configFile["rules"][0]["actions"][1]["i2c_compare_bytes"]["values"][0] = "0xG1"; EXPECT_JSON_INVALID(configFile, "Validation failed.", "'0xG1' does not match '^0x[0-9A-Fa-f]{2}$'"); } // Invalid: test i2c_compare_bytes with property masks invalid hex digit. { json configFile = i2cCompareBytesFile; configFile["rules"][0]["actions"][1]["i2c_compare_bytes"]["masks"][0] = "0xG1"; EXPECT_JSON_INVALID(configFile, "Validation failed.", "'0xG1' does not match '^0x[0-9A-Fa-f]{2}$'"); } } TEST(ValidateRegulatorsConfigTest, I2CInterface) { // Valid: test i2c_interface. { json configFile = validConfigFile; EXPECT_JSON_VALID(configFile); } // Invalid: testi2c_interface with no bus. { json configFile = validConfigFile; configFile["chassis"][0]["devices"][0]["i2c_interface"].erase("bus"); EXPECT_JSON_INVALID(configFile, "Validation failed.", "'bus' is a required property"); } // Invalid: test i2c_interface with no address. { json configFile = validConfigFile; configFile["chassis"][0]["devices"][0]["i2c_interface"].erase( "address"); EXPECT_JSON_INVALID(configFile, "Validation failed.", "'address' is a required property"); } // Invalid: test i2c_interface with property bus wrong type. { json configFile = validConfigFile; configFile["chassis"][0]["devices"][0]["i2c_interface"]["bus"] = true; EXPECT_JSON_INVALID(configFile, "Validation failed.", "True is not of type 'integer'"); } // Invalid: test i2c_interface with property address wrong // type. { json configFile = validConfigFile; configFile["chassis"][0]["devices"][0]["i2c_interface"]["address"] = true; EXPECT_JSON_INVALID(configFile, "Validation failed.", "True is not of type 'string'"); } // Invalid: test i2c_interface with property bus less than // 0. { json configFile = validConfigFile; configFile["chassis"][0]["devices"][0]["i2c_interface"]["bus"] = -1; EXPECT_JSON_INVALID(configFile, "Validation failed.", "-1 is less than the minimum of 0"); } // Invalid: test i2c_interface with property address wrong // format. { json configFile = validConfigFile; configFile["chassis"][0]["devices"][0]["i2c_interface"]["address"] = "0x700"; EXPECT_JSON_INVALID(configFile, "Validation failed.", "'0x700' does not match '^0x[0-9A-Fa-f]{2}$'"); } } TEST(ValidateRegulatorsConfigTest, I2CWriteBit) { json i2cWriteBitFile = validConfigFile; i2cWriteBitFile["rules"][0]["actions"][1]["i2c_write_bit"]["register"] = "0xA0"; i2cWriteBitFile["rules"][0]["actions"][1]["i2c_write_bit"]["position"] = 3; i2cWriteBitFile["rules"][0]["actions"][1]["i2c_write_bit"]["value"] = 1; // Valid: test rule actions i2c_write_bit. { json configFile = i2cWriteBitFile; EXPECT_JSON_VALID(configFile); } // Invalid: test i2c_write_bit with no register. { json configFile = i2cWriteBitFile; configFile["rules"][0]["actions"][1]["i2c_write_bit"].erase("register"); EXPECT_JSON_INVALID(configFile, "Validation failed.", "'register' is a required property"); } // Invalid: test i2c_write_bit with no position. { json configFile = i2cWriteBitFile; configFile["rules"][0]["actions"][1]["i2c_write_bit"].erase("position"); EXPECT_JSON_INVALID(configFile, "Validation failed.", "'position' is a required property"); } // Invalid: test i2c_write_bit with no value. { json configFile = i2cWriteBitFile; configFile["rules"][0]["actions"][1]["i2c_write_bit"].erase("value"); EXPECT_JSON_INVALID(configFile, "Validation failed.", "'value' is a required property"); } // Invalid: test i2c_write_bit with register wrong type. { json configFile = i2cWriteBitFile; configFile["rules"][0]["actions"][1]["i2c_write_bit"]["register"] = 1; EXPECT_JSON_INVALID(configFile, "Validation failed.", "1 is not of type 'string'"); } // Invalid: test i2c_write_bit with register wrong format. { json configFile = i2cWriteBitFile; configFile["rules"][0]["actions"][1]["i2c_write_bit"]["register"] = "0xA00"; EXPECT_JSON_INVALID(configFile, "Validation failed.", "'0xA00' does not match '^0x[0-9A-Fa-f]{2}$'"); } // Invalid: test i2c_write_bit with position wrong type. { json configFile = i2cWriteBitFile; configFile["rules"][0]["actions"][1]["i2c_write_bit"]["position"] = 3.1; EXPECT_JSON_INVALID(configFile, "Validation failed.", "3.1 is not of type 'integer'"); } // Invalid: test i2c_write_bit with position greater than 7. { json configFile = i2cWriteBitFile; configFile["rules"][0]["actions"][1]["i2c_write_bit"]["position"] = 8; EXPECT_JSON_INVALID(configFile, "Validation failed.", "8 is greater than the maximum of 7"); } // Invalid: test i2c_write_bit with position less than 0. { json configFile = i2cWriteBitFile; configFile["rules"][0]["actions"][1]["i2c_write_bit"]["position"] = -1; EXPECT_JSON_INVALID(configFile, "Validation failed.", "-1 is less than the minimum of 0"); } // Invalid: test i2c_write_bit with value wrong type. { json configFile = i2cWriteBitFile; configFile["rules"][0]["actions"][1]["i2c_write_bit"]["value"] = "1"; EXPECT_JSON_INVALID(configFile, "Validation failed.", "'1' is not of type 'integer'"); } // Invalid: test i2c_write_bit with value greater than 1. { json configFile = i2cWriteBitFile; configFile["rules"][0]["actions"][1]["i2c_write_bit"]["value"] = 2; EXPECT_JSON_INVALID(configFile, "Validation failed.", "2 is greater than the maximum of 1"); } // Invalid: test i2c_write_bit with value less than 0. { json configFile = i2cWriteBitFile; configFile["rules"][0]["actions"][1]["i2c_write_bit"]["value"] = -1; EXPECT_JSON_INVALID(configFile, "Validation failed.", "-1 is less than the minimum of 0"); } } TEST(ValidateRegulatorsConfigTest, I2CWriteByte) { json i2cWriteByteFile = validConfigFile; i2cWriteByteFile["rules"][0]["actions"][1]["i2c_write_byte"]["register"] = "0x82"; i2cWriteByteFile["rules"][0]["actions"][1]["i2c_write_byte"]["value"] = "0x40"; i2cWriteByteFile["rules"][0]["actions"][1]["i2c_write_byte"]["mask"] = "0x7F"; // Valid: test i2c_write_byte with all properties. { json configFile = i2cWriteByteFile; EXPECT_JSON_VALID(configFile); } // Valid: test i2c_write_byte with all required properties. { json configFile = i2cWriteByteFile; configFile["rules"][0]["actions"][1]["i2c_write_byte"].erase("mask"); EXPECT_JSON_VALID(configFile); } // Invalid: test i2c_write_byte with no register. { json configFile = i2cWriteByteFile; configFile["rules"][0]["actions"][1]["i2c_write_byte"].erase( "register"); EXPECT_JSON_INVALID(configFile, "Validation failed.", "'register' is a required property"); } // Invalid: test i2c_write_byte with no value. { json configFile = i2cWriteByteFile; configFile["rules"][0]["actions"][1]["i2c_write_byte"].erase("value"); EXPECT_JSON_INVALID(configFile, "Validation failed.", "'value' is a required property"); } // Invalid: test i2c_write_byte with property register wrong type. { json configFile = i2cWriteByteFile; configFile["rules"][0]["actions"][1]["i2c_write_byte"]["register"] = 1; EXPECT_JSON_INVALID(configFile, "Validation failed.", "1 is not of type 'string'"); } // Invalid: test i2c_write_byte with property value wrong type. { json configFile = i2cWriteByteFile; configFile["rules"][0]["actions"][1]["i2c_write_byte"]["value"] = 1; EXPECT_JSON_INVALID(configFile, "Validation failed.", "1 is not of type 'string'"); } // Invalid: test i2c_write_byte with property mask wrong type. { json configFile = i2cWriteByteFile; configFile["rules"][0]["actions"][1]["i2c_write_byte"]["mask"] = 1; EXPECT_JSON_INVALID(configFile, "Validation failed.", "1 is not of type 'string'"); } // Invalid: test i2c_write_byte with property register more than 2 hex // digits. { json configFile = i2cWriteByteFile; configFile["rules"][0]["actions"][1]["i2c_write_byte"]["register"] = "0x820"; EXPECT_JSON_INVALID(configFile, "Validation failed.", "'0x820' does not match '^0x[0-9A-Fa-f]{2}$'"); } // Invalid: test i2c_write_byte with property value more than 2 hex // digits. { json configFile = i2cWriteByteFile; configFile["rules"][0]["actions"][1]["i2c_write_byte"]["value"] = "0x820"; EXPECT_JSON_INVALID(configFile, "Validation failed.", "'0x820' does not match '^0x[0-9A-Fa-f]{2}$'"); } // Invalid: test i2c_write_byte with property mask more than 2 hex digits. { json configFile = i2cWriteByteFile; configFile["rules"][0]["actions"][1]["i2c_write_byte"]["mask"] = "0x820"; EXPECT_JSON_INVALID(configFile, "Validation failed.", "'0x820' does not match '^0x[0-9A-Fa-f]{2}$'"); } // Invalid: test i2c_write_byte with property register less than 2 hex // digits. { json configFile = i2cWriteByteFile; configFile["rules"][0]["actions"][1]["i2c_write_byte"]["register"] = "0x8"; EXPECT_JSON_INVALID(configFile, "Validation failed.", "'0x8' does not match '^0x[0-9A-Fa-f]{2}$'"); } // Invalid: test i2c_write_byte with property value less than 2 hex // digits. { json configFile = i2cWriteByteFile; configFile["rules"][0]["actions"][1]["i2c_write_byte"]["value"] = "0x8"; EXPECT_JSON_INVALID(configFile, "Validation failed.", "'0x8' does not match '^0x[0-9A-Fa-f]{2}$'"); } // Invalid: test i2c_write_byte with property mask less than 2 hex digits. { json configFile = i2cWriteByteFile; configFile["rules"][0]["actions"][1]["i2c_write_byte"]["mask"] = "0x8"; EXPECT_JSON_INVALID(configFile, "Validation failed.", "'0x8' does not match '^0x[0-9A-Fa-f]{2}$'"); } // Invalid: test i2c_write_byte with property register no leading prefix. { json configFile = i2cWriteByteFile; configFile["rules"][0]["actions"][1]["i2c_write_byte"]["register"] = "82"; EXPECT_JSON_INVALID(configFile, "Validation failed.", "'82' does not match '^0x[0-9A-Fa-f]{2}$'"); } // Invalid: test i2c_write_byte with property value no leading prefix. { json configFile = i2cWriteByteFile; configFile["rules"][0]["actions"][1]["i2c_write_byte"]["value"] = "82"; EXPECT_JSON_INVALID(configFile, "Validation failed.", "'82' does not match '^0x[0-9A-Fa-f]{2}$'"); } // Invalid: test i2c_write_byte with property mask no leading prefix. { json configFile = i2cWriteByteFile; configFile["rules"][0]["actions"][1]["i2c_write_byte"]["mask"] = "82"; EXPECT_JSON_INVALID(configFile, "Validation failed.", "'82' does not match '^0x[0-9A-Fa-f]{2}$'"); } // Invalid: test i2c_write_byte with property register invalid hex digit. { json configFile = i2cWriteByteFile; configFile["rules"][0]["actions"][1]["i2c_write_byte"]["register"] = "0xG1"; EXPECT_JSON_INVALID(configFile, "Validation failed.", "'0xG1' does not match '^0x[0-9A-Fa-f]{2}$'"); } // Invalid: test i2c_write_byte with property value invalid hex digit. { json configFile = i2cWriteByteFile; configFile["rules"][0]["actions"][1]["i2c_write_byte"]["value"] = "0xG1"; EXPECT_JSON_INVALID(configFile, "Validation failed.", "'0xG1' does not match '^0x[0-9A-Fa-f]{2}$'"); } // Invalid: test i2c_write_byte with property mask invalid hex digit. { json configFile = i2cWriteByteFile; configFile["rules"][0]["actions"][1]["i2c_write_byte"]["mask"] = "0xG1"; EXPECT_JSON_INVALID(configFile, "Validation failed.", "'0xG1' does not match '^0x[0-9A-Fa-f]{2}$'"); } } TEST(ValidateRegulatorsConfigTest, I2CWriteBytes) { json i2cWriteBytesFile = validConfigFile; i2cWriteBytesFile["rules"][0]["actions"][1]["i2c_write_bytes"]["register"] = "0x82"; i2cWriteBytesFile["rules"][0]["actions"][1]["i2c_write_bytes"]["values"] = { "0x02", "0x73"}; i2cWriteBytesFile["rules"][0]["actions"][1]["i2c_write_bytes"]["masks"] = { "0x7F", "0x7F"}; // Valid: test i2c_write_bytes. { json configFile = i2cWriteBytesFile; EXPECT_JSON_VALID(configFile); } // Valid: test i2c_write_bytes with all required properties. { json configFile = i2cWriteBytesFile; configFile["rules"][0]["actions"][1]["i2c_write_bytes"].erase("masks"); EXPECT_JSON_VALID(configFile); } // Invalid: test i2c_write_bytes with no register. { json configFile = i2cWriteBytesFile; configFile["rules"][0]["actions"][1]["i2c_write_bytes"].erase( "register"); EXPECT_JSON_INVALID(configFile, "Validation failed.", "'register' is a required property"); } // Invalid: test i2c_write_bytes with no values. { json configFile = i2cWriteBytesFile; configFile["rules"][0]["actions"][1]["i2c_write_bytes"].erase("values"); EXPECT_JSON_INVALID(configFile, "Validation failed.", "'values' is a required property"); } // Invalid: test i2c_write_bytes with property values as empty array. { json configFile = i2cWriteBytesFile; configFile["rules"][0]["actions"][1]["i2c_write_bytes"]["values"] = json::array(); EXPECT_JSON_INVALID(configFile, "Validation failed.", "[] is too short"); } // Invalid: test i2c_write_bytes with property masks as empty array. { json configFile = i2cWriteBytesFile; configFile["rules"][0]["actions"][1]["i2c_write_bytes"]["masks"] = json::array(); EXPECT_JSON_INVALID(configFile, "Validation failed.", "[] is too short"); } // Invalid: test i2c_write_bytes with property register wrong type. { json configFile = i2cWriteBytesFile; configFile["rules"][0]["actions"][1]["i2c_write_bytes"]["register"] = 1; EXPECT_JSON_INVALID(configFile, "Validation failed.", "1 is not of type 'string'"); } // Invalid: test i2c_write_bytes with property values wrong type. { json configFile = i2cWriteBytesFile; configFile["rules"][0]["actions"][1]["i2c_write_bytes"]["values"] = 1; EXPECT_JSON_INVALID(configFile, "Validation failed.", "1 is not of type 'array'"); } // Invalid: test i2c_write_bytes with property masks wrong type. { json configFile = i2cWriteBytesFile; configFile["rules"][0]["actions"][1]["i2c_write_bytes"]["masks"] = 1; EXPECT_JSON_INVALID(configFile, "Validation failed.", "1 is not of type 'array'"); } // Invalid: test i2c_write_bytes with property register more than 2 hex // digits. { json configFile = i2cWriteBytesFile; configFile["rules"][0]["actions"][1]["i2c_write_bytes"]["register"] = "0x820"; EXPECT_JSON_INVALID(configFile, "Validation failed.", "'0x820' does not match '^0x[0-9A-Fa-f]{2}$'"); } // Invalid: test i2c_write_bytes with property values more than 2 hex // digits. { json configFile = i2cWriteBytesFile; configFile["rules"][0]["actions"][1]["i2c_write_bytes"]["values"][0] = "0x820"; EXPECT_JSON_INVALID(configFile, "Validation failed.", "'0x820' does not match '^0x[0-9A-Fa-f]{2}$'"); } // Invalid: test i2c_write_bytes with property masks more than 2 hex // digits. { json configFile = i2cWriteBytesFile; configFile["rules"][0]["actions"][1]["i2c_write_bytes"]["masks"][0] = "0x820"; EXPECT_JSON_INVALID(configFile, "Validation failed.", "'0x820' does not match '^0x[0-9A-Fa-f]{2}$'"); } // Invalid: test i2c_write_bytes with property register less than 2 hex // digits. { json configFile = i2cWriteBytesFile; configFile["rules"][0]["actions"][1]["i2c_write_bytes"]["register"] = "0x8"; EXPECT_JSON_INVALID(configFile, "Validation failed.", "'0x8' does not match '^0x[0-9A-Fa-f]{2}$'"); } // Invalid: test i2c_write_bytes with property values less than 2 hex // digits. { json configFile = i2cWriteBytesFile; configFile["rules"][0]["actions"][1]["i2c_write_bytes"]["values"][0] = "0x8"; EXPECT_JSON_INVALID(configFile, "Validation failed.", "'0x8' does not match '^0x[0-9A-Fa-f]{2}$'"); } // Invalid: test i2c_write_bytes with property masks less than 2 hex // digits. { json configFile = i2cWriteBytesFile; configFile["rules"][0]["actions"][1]["i2c_write_bytes"]["masks"][0] = "0x8"; EXPECT_JSON_INVALID(configFile, "Validation failed.", "'0x8' does not match '^0x[0-9A-Fa-f]{2}$'"); } // Invalid: test i2c_write_bytes with property register no leading prefix. { json configFile = i2cWriteBytesFile; configFile["rules"][0]["actions"][1]["i2c_write_bytes"]["register"] = "82"; EXPECT_JSON_INVALID(configFile, "Validation failed.", "'82' does not match '^0x[0-9A-Fa-f]{2}$'"); } // Invalid: test i2c_write_bytes with property values no leading prefix. { json configFile = i2cWriteBytesFile; configFile["rules"][0]["actions"][1]["i2c_write_bytes"]["values"][0] = "82"; EXPECT_JSON_INVALID(configFile, "Validation failed.", "'82' does not match '^0x[0-9A-Fa-f]{2}$'"); } // Invalid: test i2c_write_bytes with property masks no leading prefix. { json configFile = i2cWriteBytesFile; configFile["rules"][0]["actions"][1]["i2c_write_bytes"]["masks"][0] = "82"; EXPECT_JSON_INVALID(configFile, "Validation failed.", "'82' does not match '^0x[0-9A-Fa-f]{2}$'"); } // Invalid: test i2c_write_bytes with property register invalid hex digit. { json configFile = i2cWriteBytesFile; configFile["rules"][0]["actions"][1]["i2c_write_bytes"]["register"] = "0xG1"; EXPECT_JSON_INVALID(configFile, "Validation failed.", "'0xG1' does not match '^0x[0-9A-Fa-f]{2}$'"); } // Invalid: test i2c_write_bytes with property values invalid hex digit. { json configFile = i2cWriteBytesFile; configFile["rules"][0]["actions"][1]["i2c_write_bytes"]["values"][0] = "0xG1"; EXPECT_JSON_INVALID(configFile, "Validation failed.", "'0xG1' does not match '^0x[0-9A-Fa-f]{2}$'"); } // Invalid: test i2c_write_bytes with property masks invalid hex digit. { json configFile = i2cWriteBytesFile; configFile["rules"][0]["actions"][1]["i2c_write_bytes"]["masks"][0] = "0xG1"; EXPECT_JSON_INVALID(configFile, "Validation failed.", "'0xG1' does not match '^0x[0-9A-Fa-f]{2}$'"); } } TEST(ValidateRegulatorsConfigTest, If) { json ifFile = validConfigFile; ifFile["rules"][4]["actions"][0]["if"]["condition"]["run_rule"] = "set_voltage_rule"; ifFile["rules"][4]["actions"][0]["if"]["then"][0]["run_rule"] = "read_sensors_rule"; ifFile["rules"][4]["actions"][0]["if"]["else"][0]["run_rule"] = "read_sensors_rule"; ifFile["rules"][4]["id"] = "rule_if"; // Valid: test if. { json configFile = ifFile; EXPECT_JSON_VALID(configFile); } // Valid: test if with required properties. { json configFile = ifFile; configFile["rules"][4]["actions"][0]["if"].erase("else"); EXPECT_JSON_VALID(configFile); } // Invalid: test if with no property condition. { json configFile = ifFile; configFile["rules"][4]["actions"][0]["if"].erase("condition"); EXPECT_JSON_INVALID(configFile, "Validation failed.", "'condition' is a required property"); } // Invalid: test if with no property then. { json configFile = ifFile; configFile["rules"][4]["actions"][0]["if"].erase("then"); EXPECT_JSON_INVALID(configFile, "Validation failed.", "'then' is a required property"); } // Invalid: test if with property then empty array. { json configFile = ifFile; configFile["rules"][4]["actions"][0]["if"]["then"] = json::array(); EXPECT_JSON_INVALID(configFile, "Validation failed.", "[] is too short"); } // Invalid: test if with property else empty array. { json configFile = ifFile; configFile["rules"][4]["actions"][0]["if"]["else"] = json::array(); EXPECT_JSON_INVALID(configFile, "Validation failed.", "[] is too short"); } // Invalid: test if with property condition wrong type. { json configFile = ifFile; configFile["rules"][4]["actions"][0]["if"]["condition"] = 1; EXPECT_JSON_INVALID(configFile, "Validation failed.", "1 is not of type 'object'"); } // Invalid: test if with property then wrong type. { json configFile = ifFile; configFile["rules"][4]["actions"][0]["if"]["then"] = 1; EXPECT_JSON_INVALID(configFile, "Validation failed.", "1 is not of type 'array'"); } // Invalid: test if with property else wrong type. { json configFile = ifFile; configFile["rules"][4]["actions"][0]["if"]["else"] = 1; EXPECT_JSON_INVALID(configFile, "Validation failed.", "1 is not of type 'array'"); } } TEST(ValidateRegulatorsConfigTest, LogPhaseFault) { json initialFile = validConfigFile; initialFile["rules"][0]["actions"][1]["log_phase_fault"]["type"] = "n"; // Valid: All required properties { json configFile = initialFile; EXPECT_JSON_VALID(configFile); } // Invalid: type not specified { json configFile = initialFile; configFile["rules"][0]["actions"][1]["log_phase_fault"].erase("type"); EXPECT_JSON_INVALID(configFile, "Validation failed.", "'type' is a required property"); } // Invalid: invalid property specified { json configFile = initialFile; configFile["rules"][0]["actions"][1]["log_phase_fault"]["foo"] = true; EXPECT_JSON_INVALID( configFile, "Validation failed.", "Additional properties are not allowed ('foo' was unexpected)"); } // Invalid: type has wrong data type { json configFile = initialFile; configFile["rules"][0]["actions"][1]["log_phase_fault"]["type"] = true; EXPECT_JSON_INVALID(configFile, "Validation failed.", "True is not of type 'string'"); } // Invalid: type has invalid value { json configFile = initialFile; configFile["rules"][0]["actions"][1]["log_phase_fault"]["type"] = "n+2"; EXPECT_JSON_INVALID(configFile, "Validation failed.", "'n+2' is not one of ['n+1', 'n']"); } } TEST(ValidateRegulatorsConfigTest, Not) { json notFile = validConfigFile; notFile["rules"][0]["actions"][1]["not"]["i2c_compare_byte"]["register"] = "0xA0"; notFile["rules"][0]["actions"][1]["not"]["i2c_compare_byte"]["value"] = "0xFF"; // Valid: test not. { json configFile = notFile; EXPECT_JSON_VALID(configFile); } // Invalid: test not with wrong type. { json configFile = notFile; configFile["rules"][0]["actions"][1]["not"] = 1; EXPECT_JSON_INVALID(configFile, "Validation failed.", "1 is not of type 'object'"); } } TEST(ValidateRegulatorsConfigTest, Or) { json orFile = validConfigFile; orFile["rules"][0]["actions"][1]["or"][0]["i2c_compare_byte"]["register"] = "0xA0"; orFile["rules"][0]["actions"][1]["or"][0]["i2c_compare_byte"]["value"] = "0x00"; orFile["rules"][0]["actions"][1]["or"][1]["i2c_compare_byte"]["register"] = "0xA1"; orFile["rules"][0]["actions"][1]["or"][1]["i2c_compare_byte"]["value"] = "0x00"; // Valid: test or. { json configFile = orFile; EXPECT_JSON_VALID(configFile); } // Invalid: test or with empty array. { json configFile = orFile; configFile["rules"][0]["actions"][1]["or"] = json::array(); EXPECT_JSON_INVALID(configFile, "Validation failed.", "[] is too short"); } // Invalid: test or with wrong type. { json configFile = orFile; configFile["rules"][0]["actions"][1]["or"] = 1; EXPECT_JSON_INVALID(configFile, "Validation failed.", "1 is not of type 'array'"); } } TEST(ValidateRegulatorsConfigTest, PhaseFaultDetection) { json initialFile = validConfigFile; initialFile["chassis"][0]["devices"][0]["phase_fault_detection"] ["rule_id"] = "detect_phase_faults_rule"; // Valid: comments specified { json configFile = initialFile; configFile["chassis"][0]["devices"][0]["phase_fault_detection"] ["comments"][0] = "Detect phase faults"; EXPECT_JSON_VALID(configFile); } // Valid: device_id specified { json configFile = initialFile; configFile["chassis"][0]["devices"][0]["phase_fault_detection"] ["device_id"] = "vdd_regulator"; EXPECT_JSON_VALID(configFile); } // Valid: rule_id specified { json configFile = initialFile; EXPECT_JSON_VALID(configFile); } // Valid: actions specified { json configFile = initialFile; configFile["chassis"][0]["devices"][0]["phase_fault_detection"].erase( "rule_id"); configFile["chassis"][0]["devices"][0]["phase_fault_detection"] ["actions"][0]["run_rule"] = "detect_phase_faults_rule"; EXPECT_JSON_VALID(configFile); } // Invalid: rule_id and actions specified { json configFile = initialFile; configFile["chassis"][0]["devices"][0]["phase_fault_detection"] ["actions"][0]["run_rule"] = "detect_phase_faults_rule"; EXPECT_JSON_INVALID(configFile, "Validation failed.", ""); } // Invalid: neither rule_id nor actions specified { json configFile = initialFile; configFile["chassis"][0]["devices"][0]["phase_fault_detection"].erase( "rule_id"); EXPECT_JSON_INVALID(configFile, "Validation failed.", "{} is not valid under any of the given schemas"); } // Invalid: comments has wrong data type { json configFile = initialFile; configFile["chassis"][0]["devices"][0]["phase_fault_detection"] ["comments"] = true; EXPECT_JSON_INVALID(configFile, "Validation failed.", "True is not of type 'array'"); } // Invalid: device_id has wrong data type { json configFile = initialFile; configFile["chassis"][0]["devices"][0]["phase_fault_detection"] ["device_id"] = true; EXPECT_JSON_INVALID(configFile, "Validation failed.", "True is not of type 'string'"); } // Invalid: rule_id has wrong data type { json configFile = initialFile; configFile["chassis"][0]["devices"][0]["phase_fault_detection"] ["rule_id"] = true; EXPECT_JSON_INVALID(configFile, "Validation failed.", "True is not of type 'string'"); } // Invalid: actions has wrong data type { json configFile = initialFile; configFile["chassis"][0]["devices"][0]["phase_fault_detection"].erase( "rule_id"); configFile["chassis"][0]["devices"][0]["phase_fault_detection"] ["actions"] = true; EXPECT_JSON_INVALID(configFile, "Validation failed.", "True is not of type 'array'"); } // Invalid: device_id has invalid format { json configFile = initialFile; configFile["chassis"][0]["devices"][0]["phase_fault_detection"] ["device_id"] = "id@"; EXPECT_JSON_INVALID(configFile, "Validation failed.", "'id@' does not match '^[A-Za-z0-9_]+$'"); } // Invalid: rule_id has invalid format { json configFile = initialFile; configFile["chassis"][0]["devices"][0]["phase_fault_detection"] ["rule_id"] = "id@"; EXPECT_JSON_INVALID(configFile, "Validation failed.", "'id@' does not match '^[A-Za-z0-9_]+$'"); } // Invalid: comments array is empty { json configFile = initialFile; configFile["chassis"][0]["devices"][0]["phase_fault_detection"] ["comments"] = json::array(); EXPECT_JSON_INVALID(configFile, "Validation failed.", "[] is too short"); } // Invalid: actions array is empty { json configFile = initialFile; configFile["chassis"][0]["devices"][0]["phase_fault_detection"].erase( "rule_id"); configFile["chassis"][0]["devices"][0]["phase_fault_detection"] ["actions"] = json::array(); EXPECT_JSON_INVALID(configFile, "Validation failed.", "[] is too short"); } } TEST(ValidateRegulatorsConfigTest, PmbusReadSensor) { json pmbusReadSensorFile = validConfigFile; pmbusReadSensorFile["rules"][0]["actions"][1]["pmbus_read_sensor"]["type"] = "vout"; pmbusReadSensorFile["rules"][0]["actions"][1]["pmbus_read_sensor"] ["command"] = "0x8B"; pmbusReadSensorFile["rules"][0]["actions"][1]["pmbus_read_sensor"] ["format"] = "linear_16"; pmbusReadSensorFile["rules"][0]["actions"][1]["pmbus_read_sensor"] ["exponent"] = -8; // Valid: test pmbus_read_sensor. { json configFile = pmbusReadSensorFile; EXPECT_JSON_VALID(configFile); } // Valid: test pmbus_read_sensor with required properties. { json configFile = pmbusReadSensorFile; configFile["rules"][0]["actions"][1]["pmbus_read_sensor"].erase( "exponent"); EXPECT_JSON_VALID(configFile); } // Invalid: test pmbus_read_sensor with no type. { json configFile = pmbusReadSensorFile; configFile["rules"][0]["actions"][1]["pmbus_read_sensor"].erase("type"); EXPECT_JSON_INVALID(configFile, "Validation failed.", "'type' is a required property"); } // Invalid: test pmbus_read_sensor with no command. { json configFile = pmbusReadSensorFile; configFile["rules"][0]["actions"][1]["pmbus_read_sensor"].erase( "command"); EXPECT_JSON_INVALID(configFile, "Validation failed.", "'command' is a required property"); } // Invalid: test pmbus_read_sensor with no format. { json configFile = pmbusReadSensorFile; configFile["rules"][0]["actions"][1]["pmbus_read_sensor"].erase( "format"); EXPECT_JSON_INVALID(configFile, "Validation failed.", "'format' is a required property"); } // Invalid: test pmbus_read_sensor with property type wrong type. { json configFile = pmbusReadSensorFile; configFile["rules"][0]["actions"][1]["pmbus_read_sensor"]["type"] = true; EXPECT_JSON_INVALID(configFile, "Validation failed.", "True is not of type 'string'"); } // Invalid: test pmbus_read_sensor with property command wrong type. { json configFile = pmbusReadSensorFile; configFile["rules"][0]["actions"][1]["pmbus_read_sensor"]["command"] = true; EXPECT_JSON_INVALID(configFile, "Validation failed.", "True is not of type 'string'"); } // Invalid: test pmbus_read_sensor with property format wrong type. { json configFile = pmbusReadSensorFile; configFile["rules"][0]["actions"][1]["pmbus_read_sensor"]["format"] = true; EXPECT_JSON_INVALID(configFile, "Validation failed.", "True is not of type 'string'"); } // Invalid: test pmbus_read_sensor with property exponent wrong type. { json configFile = pmbusReadSensorFile; configFile["rules"][0]["actions"][1]["pmbus_read_sensor"]["exponent"] = true; EXPECT_JSON_INVALID(configFile, "Validation failed.", "True is not of type 'integer'"); } // Invalid: test pmbus_read_sensor with property type wrong format. { json configFile = pmbusReadSensorFile; configFile["rules"][0]["actions"][1]["pmbus_read_sensor"]["type"] = "foo"; EXPECT_JSON_INVALID( configFile, "Validation failed.", "'foo' is not one of ['iout', 'iout_peak', 'iout_valley', " "'pout', 'temperature', 'temperature_peak', 'vout', " "'vout_peak', 'vout_valley']"); } // Invalid: test pmbus_read_sensor with property command wrong format. { json configFile = pmbusReadSensorFile; configFile["rules"][0]["actions"][1]["pmbus_read_sensor"]["command"] = "0x8B0"; EXPECT_JSON_INVALID(configFile, "Validation failed.", "'0x8B0' does not match '^0x[0-9a-fA-F]{2}$'"); } // Invalid: test pmbus_read_sensor with property format wrong format. { json configFile = pmbusReadSensorFile; configFile["rules"][0]["actions"][1]["pmbus_read_sensor"]["format"] = "foo"; EXPECT_JSON_INVALID(configFile, "Validation failed.", "'foo' is not one of ['linear_11', 'linear_16']"); } } TEST(ValidateRegulatorsConfigTest, PmbusWriteVoutCommand) { json pmbusWriteVoutCommandFile = validConfigFile; pmbusWriteVoutCommandFile["rules"][0]["actions"][1] ["pmbus_write_vout_command"]["volts"] = 1.03; pmbusWriteVoutCommandFile["rules"][0]["actions"][1] ["pmbus_write_vout_command"]["format"] = "linear"; pmbusWriteVoutCommandFile["rules"][0]["actions"][1] ["pmbus_write_vout_command"]["exponent"] = -8; pmbusWriteVoutCommandFile["rules"][0]["actions"][1] ["pmbus_write_vout_command"]["is_verified"] = true; // Valid: test pmbus_write_vout_command. { json configFile = pmbusWriteVoutCommandFile; EXPECT_JSON_VALID(configFile); } // Valid: test pmbus_write_vout_command with required properties. { json configFile = pmbusWriteVoutCommandFile; configFile["rules"][0]["actions"][1]["pmbus_write_vout_command"].erase( "volts"); configFile["rules"][0]["actions"][1]["pmbus_write_vout_command"].erase( "exponent"); configFile["rules"][0]["actions"][1]["pmbus_write_vout_command"].erase( "is_verified"); EXPECT_JSON_VALID(configFile); } // Invalid: test pmbus_write_vout_command with no format. { json configFile = pmbusWriteVoutCommandFile; configFile["rules"][0]["actions"][1]["pmbus_write_vout_command"].erase( "format"); EXPECT_JSON_INVALID(configFile, "Validation failed.", "'format' is a required property"); } // Invalid: test pmbus_write_vout_command with property volts wrong type. { json configFile = pmbusWriteVoutCommandFile; configFile["rules"][0]["actions"][1]["pmbus_write_vout_command"] ["volts"] = true; EXPECT_JSON_INVALID(configFile, "Validation failed.", "True is not of type 'number'"); } // Invalid: test pmbus_write_vout_command with property format wrong type. { json configFile = pmbusWriteVoutCommandFile; configFile["rules"][0]["actions"][1]["pmbus_write_vout_command"] ["format"] = true; EXPECT_JSON_INVALID(configFile, "Validation failed.", "True is not of type 'string'"); } // Invalid: test pmbus_write_vout_command with property exponent wrong type. { json configFile = pmbusWriteVoutCommandFile; configFile["rules"][0]["actions"][1]["pmbus_write_vout_command"] ["exponent"] = 1.3; EXPECT_JSON_INVALID(configFile, "Validation failed.", "1.3 is not of type 'integer'"); } // Invalid: test pmbus_write_vout_command with property is_verified wrong // type. { json configFile = pmbusWriteVoutCommandFile; configFile["rules"][0]["actions"][1]["pmbus_write_vout_command"] ["is_verified"] = 1; EXPECT_JSON_INVALID(configFile, "Validation failed.", "1 is not of type 'boolean'"); } // Invalid: test pmbus_write_vout_command with property format wrong format. { json configFile = pmbusWriteVoutCommandFile; configFile["rules"][0]["actions"][1]["pmbus_write_vout_command"] ["format"] = "foo"; EXPECT_JSON_INVALID(configFile, "Validation failed.", "'foo' is not one of ['linear']"); } } TEST(ValidateRegulatorsConfigTest, PresenceDetection) { json presenceDetectionFile = validConfigFile; presenceDetectionFile["chassis"][0]["devices"][0]["presence_detection"] ["comments"][0] = "Regulator is only present if CPU3 is present"; presenceDetectionFile["chassis"][0]["devices"][0]["presence_detection"] ["rule_id"] = "detect_presence_rule"; // Valid: test presence_detection with only property rule_id. { json configFile = presenceDetectionFile; EXPECT_JSON_VALID(configFile); } // Valid: test presence_detection with only property actions. { json configFile = presenceDetectionFile; configFile["chassis"][0]["devices"][0]["presence_detection"].erase( "rule_id"); configFile["chassis"][0]["devices"][0]["presence_detection"]["actions"] [0]["compare_presence"]["fru"] = "system/chassis/motherboard/cpu3"; configFile["chassis"][0]["devices"][0]["presence_detection"]["actions"] [0]["compare_presence"]["value"] = true; configFile["chassis"][0]["devices"][0]["presence_detection"].erase( "comments"); EXPECT_JSON_VALID(configFile); } // Invalid: test presence_detection with both property rule_id and actions. { json configFile = presenceDetectionFile; configFile["chassis"][0]["devices"][0]["presence_detection"]["actions"] [0]["compare_presence"]["fru"] = "system/chassis/motherboard/cpu3"; configFile["chassis"][0]["devices"][0]["presence_detection"]["actions"] [0]["compare_presence"]["value"] = true; EXPECT_JSON_INVALID(configFile, "Validation failed.", ""); } // Invalid: test presence_detection with no rule_id and actions. { json configFile = presenceDetectionFile; configFile["chassis"][0]["devices"][0]["presence_detection"].erase( "rule_id"); EXPECT_JSON_INVALID( configFile, "Validation failed.", "{'comments': ['Regulator is only present if CPU3 is present']} is not valid under any of the given schemas"); } // Invalid: test presence_detection with property comments wrong type. { json configFile = presenceDetectionFile; configFile["chassis"][0]["devices"][0]["presence_detection"] ["comments"] = true; EXPECT_JSON_INVALID(configFile, "Validation failed.", "True is not of type 'array'"); } // Invalid: test presence_detection with property rule_id wrong type. { json configFile = presenceDetectionFile; configFile["chassis"][0]["devices"][0]["presence_detection"] ["rule_id"] = true; EXPECT_JSON_INVALID(configFile, "Validation failed.", "True is not of type 'string'"); } // Invalid: test presence_detection with property actions wrong type. { json configFile = presenceDetectionFile; configFile["chassis"][0]["devices"][0]["presence_detection"].erase( "rule_id"); configFile["chassis"][0]["devices"][0]["presence_detection"] ["actions"] = true; EXPECT_JSON_INVALID(configFile, "Validation failed.", "True is not of type 'array'"); } // Invalid: test presence_detection with property rule_id wrong format. { json configFile = presenceDetectionFile; configFile["chassis"][0]["devices"][0]["presence_detection"] ["rule_id"] = "id@"; EXPECT_JSON_INVALID(configFile, "Validation failed.", "'id@' does not match '^[A-Za-z0-9_]+$'"); } // Invalid: test presence_detection with property comments empty array. { json configFile = presenceDetectionFile; configFile["chassis"][0]["devices"][0]["presence_detection"] ["comments"] = json::array(); EXPECT_JSON_INVALID(configFile, "Validation failed.", "[] is too short"); } // Invalid: test presence_detection with property actions empty array. { json configFile = presenceDetectionFile; configFile["chassis"][0]["devices"][0]["presence_detection"].erase( "rule_id"); configFile["chassis"][0]["devices"][0]["presence_detection"] ["actions"] = json::array(); EXPECT_JSON_INVALID(configFile, "Validation failed.", "[] is too short"); } } TEST(ValidateRegulatorsConfigTest, Rail) { // Valid: test rail. { json configFile = validConfigFile; EXPECT_JSON_VALID(configFile); } // Valid: test rail with required properties. { json configFile = validConfigFile; configFile["chassis"][0]["devices"][0]["rails"][0].erase("comments"); configFile["chassis"][0]["devices"][0]["rails"][0].erase( "configuration"); configFile["chassis"][0]["devices"][0]["rails"][0].erase( "sensor_monitoring"); EXPECT_JSON_VALID(configFile); } // Invalid: test rail with no id. { json configFile = validConfigFile; configFile["chassis"][0]["devices"][0]["rails"][0].erase("id"); EXPECT_JSON_INVALID(configFile, "Validation failed.", "'id' is a required property"); } // Invalid: test rail with comments wrong type. { json configFile = validConfigFile; configFile["chassis"][0]["devices"][0]["rails"][0]["comments"] = true; EXPECT_JSON_INVALID(configFile, "Validation failed.", "True is not of type 'array'"); } // Invalid: test rail with id wrong type. { json configFile = validConfigFile; configFile["chassis"][0]["devices"][0]["rails"][0]["id"] = true; EXPECT_JSON_INVALID(configFile, "Validation failed.", "True is not of type 'string'"); } // Invalid: test rail with configuration wrong type. { json configFile = validConfigFile; configFile["chassis"][0]["devices"][0]["rails"][0]["configuration"] = true; EXPECT_JSON_INVALID(configFile, "Validation failed.", "True is not of type 'object'"); } // Invalid: test rail with sensor_monitoring wrong type. { json configFile = validConfigFile; configFile["chassis"][0]["devices"][0]["rails"][0] ["sensor_monitoring"] = true; EXPECT_JSON_INVALID(configFile, "Validation failed.", "True is not of type 'object'"); } // Invalid: test rail with comments empty array. { json configFile = validConfigFile; configFile["chassis"][0]["devices"][0]["rails"][0]["comments"] = json::array(); EXPECT_JSON_INVALID(configFile, "Validation failed.", "[] is too short"); } // Invalid: test rail with id wrong format. { json configFile = validConfigFile; configFile["chassis"][0]["devices"][0]["rails"][0]["id"] = "id~"; EXPECT_JSON_INVALID(configFile, "Validation failed.", "'id~' does not match '^[A-Za-z0-9_]+$'"); } } TEST(ValidateRegulatorsConfigTest, Rule) { // valid test comments property, id property, // action property specified. { json configFile = validConfigFile; EXPECT_JSON_VALID(configFile); } // valid test rule with no comments { json configFile = validConfigFile; configFile["rules"][0].erase("comments"); EXPECT_JSON_VALID(configFile); } // invalid test comments property has invalid value type { json configFile = validConfigFile; configFile["rules"][0]["comments"] = {1}; EXPECT_JSON_INVALID(configFile, "Validation failed.", "1 is not of type 'string'"); } // invalid test rule with no ID { json configFile = validConfigFile; configFile["rules"][0].erase("id"); EXPECT_JSON_INVALID(configFile, "Validation failed.", "'id' is a required property"); } // invalid test id property has invalid value type (not string) { json configFile = validConfigFile; configFile["rules"][0]["id"] = true; EXPECT_JSON_INVALID(configFile, "Validation failed.", "True is not of type 'string'"); } // invalid test id property has invalid value { json configFile = validConfigFile; configFile["rules"][0]["id"] = "foo%"; EXPECT_JSON_INVALID(configFile, "Validation failed.", "'foo%' does not match '^[A-Za-z0-9_]+$'"); } // invalid test rule with no actions property { json configFile = validConfigFile; configFile["rules"][0].erase("actions"); EXPECT_JSON_INVALID(configFile, "Validation failed.", "'actions' is a required property"); } // valid test rule with multiple actions { json configFile = validConfigFile; configFile["rules"][0]["actions"][1]["run_rule"] = "read_sensors_rule"; EXPECT_JSON_VALID(configFile); } // invalid test actions property has invalid value type (not an array) { json configFile = validConfigFile; configFile["rules"][0]["actions"] = 1; EXPECT_JSON_INVALID(configFile, "Validation failed.", "1 is not of type 'array'"); } // invalid test actions property has invalid value of action { json configFile = validConfigFile; configFile["rules"][0]["actions"][0] = "foo"; EXPECT_JSON_INVALID(configFile, "Validation failed.", "'foo' is not of type 'object'"); } // invalid test actions property has empty array { json configFile = validConfigFile; configFile["rules"][0]["actions"] = json::array(); EXPECT_JSON_INVALID(configFile, "Validation failed.", "[] is too short"); } } TEST(ValidateRegulatorsConfigTest, RunRule) { json runRuleFile = validConfigFile; runRuleFile["rules"][0]["actions"][1]["run_rule"] = "read_sensors_rule"; // Valid: test run_rule. { json configFile = runRuleFile; EXPECT_JSON_VALID(configFile); } // Invalid: test run_rule wrong type. { json configFile = runRuleFile; configFile["rules"][0]["actions"][1]["run_rule"] = true; EXPECT_JSON_INVALID(configFile, "Validation failed.", "True is not of type 'string'"); } // Invalid: test run_rule wrong format. { json configFile = runRuleFile; configFile["rules"][0]["actions"][1]["run_rule"] = "set_voltage_rule%"; EXPECT_JSON_INVALID( configFile, "Validation failed.", "'set_voltage_rule%' does not match '^[A-Za-z0-9_]+$'"); } } TEST(ValidateRegulatorsConfigTest, SensorMonitoring) { // Valid: test rails sensor_monitoring with only property rule id. { json configFile = validConfigFile; EXPECT_JSON_VALID(configFile); } // Valid: test rails sensor_monitoring with only property actions. { json configFile = validConfigFile; configFile["chassis"][0]["devices"][0]["rails"][0]["sensor_monitoring"] .erase("rule_id"); configFile["chassis"][0]["devices"][0]["rails"][0]["sensor_monitoring"] ["actions"][0]["compare_presence"]["fru"] = "system/chassis/motherboard/cpu3"; configFile["chassis"][0]["devices"][0]["rails"][0]["sensor_monitoring"] ["actions"][0]["compare_presence"]["value"] = true; configFile["chassis"][0]["devices"][0]["rails"][0]["sensor_monitoring"] ["comments"][0] = "comments"; EXPECT_JSON_VALID(configFile); } // Invalid: test rails sensor_monitoring with both property rule_id and // actions. { json configFile = validConfigFile; configFile["chassis"][0]["devices"][0]["rails"][0]["sensor_monitoring"] ["actions"][0]["compare_presence"]["fru"] = "system/chassis/motherboard/cpu3"; configFile["chassis"][0]["devices"][0]["rails"][0]["sensor_monitoring"] ["actions"][0]["compare_presence"]["value"] = true; EXPECT_JSON_INVALID(configFile, "Validation failed.", ""); } // Invalid: test rails sensor_monitoring with no rule_id and actions. { json configFile = validConfigFile; configFile["chassis"][0]["devices"][0]["rails"][0]["sensor_monitoring"] .erase("rule_id"); EXPECT_JSON_INVALID(configFile, "Validation failed.", "{} is not valid under any of the given schemas"); } // Invalid: test rails sensor_monitoring with property comments wrong type. { json configFile = validConfigFile; configFile["chassis"][0]["devices"][0]["rails"][0]["sensor_monitoring"] ["comments"] = true; EXPECT_JSON_INVALID(configFile, "Validation failed.", "True is not of type 'array'"); } // Invalid: test rails sensor_monitoring with property rule_id wrong type. { json configFile = validConfigFile; configFile["chassis"][0]["devices"][0]["rails"][0]["sensor_monitoring"] ["rule_id"] = true; EXPECT_JSON_INVALID(configFile, "Validation failed.", "True is not of type 'string'"); } // Invalid: test rails sensor_monitoring with property actions wrong type. { json configFile = validConfigFile; configFile["chassis"][0]["devices"][0]["rails"][0]["sensor_monitoring"] .erase("rule_id"); configFile["chassis"][0]["devices"][0]["rails"][0]["sensor_monitoring"] ["actions"] = true; EXPECT_JSON_INVALID(configFile, "Validation failed.", "True is not of type 'array'"); } // Invalid: test rails sensor_monitoring with property rule_id wrong format. { json configFile = validConfigFile; configFile["chassis"][0]["devices"][0]["rails"][0]["sensor_monitoring"] ["rule_id"] = "id@"; EXPECT_JSON_INVALID(configFile, "Validation failed.", "'id@' does not match '^[A-Za-z0-9_]+$'"); } // Invalid: test rails sensor_monitoring with property comments empty array. { json configFile = validConfigFile; configFile["chassis"][0]["devices"][0]["rails"][0]["sensor_monitoring"] ["comments"] = json::array(); EXPECT_JSON_INVALID(configFile, "Validation failed.", "[] is too short"); } // Invalid: test rails sensor_monitoring with property actions empty array. { json configFile = validConfigFile; configFile["chassis"][0]["devices"][0]["rails"][0]["sensor_monitoring"] .erase("rule_id"); configFile["chassis"][0]["devices"][0]["rails"][0]["sensor_monitoring"] ["actions"] = json::array(); EXPECT_JSON_INVALID(configFile, "Validation failed.", "[] is too short"); } } TEST(ValidateRegulatorsConfigTest, SetDevice) { json setDeviceFile = validConfigFile; setDeviceFile["rules"][0]["actions"][1]["set_device"] = "vdd_regulator"; // Valid: test set_device. { json configFile = setDeviceFile; EXPECT_JSON_VALID(configFile); } // Invalid: test set_device wrong type. { json configFile = setDeviceFile; configFile["rules"][0]["actions"][1]["set_device"] = true; EXPECT_JSON_INVALID(configFile, "Validation failed.", "True is not of type 'string'"); } // Invalid: test set_device wrong format. { json configFile = setDeviceFile; configFile["rules"][0]["actions"][1]["set_device"] = "io_expander2%"; EXPECT_JSON_INVALID(configFile, "Validation failed.", "'io_expander2%' does not match '^[A-Za-z0-9_]+$'"); } } TEST(ValidateRegulatorsConfigTest, DuplicateRuleID) { // Invalid: test duplicate ID in rule. { json configFile = validConfigFile; configFile["rules"][4]["id"] = "set_voltage_rule"; configFile["rules"][4]["actions"][0]["pmbus_write_vout_command"] ["format"] = "linear"; EXPECT_JSON_INVALID(configFile, "Error: Duplicate rule ID.", ""); } } TEST(ValidateRegulatorsConfigTest, DuplicateChassisNumber) { // Invalid: test duplicate number in chassis. { json configFile = validConfigFile; configFile["chassis"][1]["number"] = 1; configFile["chassis"][1]["inventory_path"] = "system/chassis2"; EXPECT_JSON_INVALID(configFile, "Error: Duplicate chassis number.", ""); } } TEST(ValidateRegulatorsConfigTest, DuplicateDeviceID) { // Invalid: test duplicate ID in device. { json configFile = validConfigFile; configFile["chassis"][0]["devices"][1]["id"] = "vdd_regulator"; configFile["chassis"][0]["devices"][1]["is_regulator"] = true; configFile["chassis"][0]["devices"][1]["fru"] = "system/chassis/motherboard/regulator1"; configFile["chassis"][0]["devices"][1]["i2c_interface"]["bus"] = 2; configFile["chassis"][0]["devices"][1]["i2c_interface"]["address"] = "0x71"; EXPECT_JSON_INVALID(configFile, "Error: Duplicate device ID.", ""); } } TEST(ValidateRegulatorsConfigTest, DuplicateRailID) { // Invalid: test duplicate ID in rail. { json configFile = validConfigFile; configFile["chassis"][0]["devices"][0]["rails"][1]["id"] = "vdd"; EXPECT_JSON_INVALID(configFile, "Error: Duplicate rail ID.", ""); } } TEST(ValidateRegulatorsConfigTest, DuplicateObjectID) { // Invalid: test duplicate object ID in device and rail. { json configFile = validConfigFile; configFile["chassis"][0]["devices"][0]["rails"][1]["id"] = "vdd_regulator"; EXPECT_JSON_INVALID(configFile, "Error: Duplicate ID.", ""); } // Invalid: test duplicate object ID in device and rule. { json configFile = validConfigFile; configFile["rules"][4]["id"] = "vdd_regulator"; configFile["rules"][4]["actions"][0]["pmbus_write_vout_command"] ["format"] = "linear"; EXPECT_JSON_INVALID(configFile, "Error: Duplicate ID.", ""); } // Invalid: test duplicate object ID in rule and rail. { json configFile = validConfigFile; configFile["chassis"][0]["devices"][0]["rails"][1]["id"] = "set_voltage_rule"; EXPECT_JSON_INVALID(configFile, "Error: Duplicate ID.", ""); } } TEST(ValidateRegulatorsConfigTest, InfiniteLoops) { // Invalid: test run_rule with infinite loop (rules run each other). { json configFile = validConfigFile; configFile["rules"][4]["actions"][0]["run_rule"] = "set_voltage_rule2"; configFile["rules"][4]["id"] = "set_voltage_rule1"; configFile["rules"][5]["actions"][0]["run_rule"] = "set_voltage_rule1"; configFile["rules"][5]["id"] = "set_voltage_rule2"; EXPECT_JSON_INVALID(configFile, "Infinite loop caused by run_rule actions.", ""); } // Invalid: test run_rule with infinite loop (rule runs itself). { json configFile = validConfigFile; configFile["rules"][4]["actions"][0]["run_rule"] = "set_voltage_rule1"; configFile["rules"][4]["id"] = "set_voltage_rule1"; EXPECT_JSON_INVALID(configFile, "Infinite loop caused by run_rule actions.", ""); } // Invalid: test run_rule with infinite loop (indirect loop). { json configFile = validConfigFile; configFile["rules"][4]["actions"][0]["run_rule"] = "set_voltage_rule2"; configFile["rules"][4]["id"] = "set_voltage_rule1"; configFile["rules"][5]["actions"][0]["run_rule"] = "set_voltage_rule3"; configFile["rules"][5]["id"] = "set_voltage_rule2"; configFile["rules"][6]["actions"][0]["run_rule"] = "set_voltage_rule1"; configFile["rules"][6]["id"] = "set_voltage_rule3"; EXPECT_JSON_INVALID(configFile, "Infinite loop caused by run_rule actions.", ""); } } TEST(ValidateRegulatorsConfigTest, RunRuleValueExists) { // Invalid: test run_rule actions specify a rule ID that does not exist. { json configFile = validConfigFile; configFile["rules"][4]["actions"][0]["run_rule"] = "set_voltage_rule2"; configFile["rules"][4]["id"] = "set_voltage_rule1"; EXPECT_JSON_INVALID(configFile, "Error: Rule ID does not exist.", ""); } } TEST(ValidateRegulatorsConfigTest, SetDeviceValueExists) { // Invalid: test set_device actions specify a device ID that does not exist. { json configFile = validConfigFile; configFile["rules"][4]["actions"][0]["set_device"] = "vdd_regulator2"; configFile["rules"][4]["id"] = "set_voltage_rule1"; EXPECT_JSON_INVALID(configFile, "Error: Device ID does not exist.", ""); } } TEST(ValidateRegulatorsConfigTest, RuleIDExists) { // Invalid: test rule_id property in configuration specifies a rule ID that // does not exist. { json configFile = validConfigFile; configFile["chassis"][0]["devices"][0]["configuration"]["rule_id"] = "set_voltage_rule2"; EXPECT_JSON_INVALID(configFile, "Error: Rule ID does not exist.", ""); } // Invalid: test rule_id property in presence_detection specifies a rule ID // that does not exist. { json configFile = validConfigFile; configFile["chassis"][0]["devices"][0]["presence_detection"] ["rule_id"] = "detect_presence_rule2"; EXPECT_JSON_INVALID(configFile, "Error: Rule ID does not exist.", ""); } // Invalid: test rule_id property in phase_fault_detection specifies a rule // ID that does not exist. { json configFile = validConfigFile; configFile["chassis"][0]["devices"][0]["phase_fault_detection"] ["rule_id"] = "detect_phase_faults_rule2"; EXPECT_JSON_INVALID(configFile, "Error: Rule ID does not exist.", ""); } // Invalid: test rule_id property in sensor_monitoring specifies a rule ID // that does not exist. { json configFile = validConfigFile; configFile["chassis"][0]["devices"][0]["rails"][0]["sensor_monitoring"] ["rule_id"] = "read_sensors_rule2"; EXPECT_JSON_INVALID(configFile, "Error: Rule ID does not exist.", ""); } } TEST(ValidateRegulatorsConfigTest, DeviceIDExists) { // Invalid: test device_id property in phase_fault_detection specifies a // device ID that does not exist. { json configFile = validConfigFile; configFile["chassis"][0]["devices"][0]["phase_fault_detection"] ["device_id"] = "vdd_regulator2"; configFile["chassis"][0]["devices"][0]["phase_fault_detection"] ["rule_id"] = "detect_phase_faults_rule"; EXPECT_JSON_INVALID(configFile, "Error: Device ID does not exist.", ""); } } TEST(ValidateRegulatorsConfigTest, NumberOfElementsInMasks) { // Invalid: test number of elements in masks not equal to number in values // in i2c_compare_bytes. { json configFile = validConfigFile; configFile["rules"][0]["actions"][1]["i2c_compare_bytes"]["register"] = "0x82"; configFile["rules"][0]["actions"][1]["i2c_compare_bytes"]["values"] = { "0x02", "0x73"}; configFile["rules"][0]["actions"][1]["i2c_compare_bytes"]["masks"] = { "0x7F"}; EXPECT_JSON_INVALID(configFile, "Error: Invalid i2c_compare_bytes action.", ""); } // Invalid: test number of elements in masks not equal to number in values // in i2c_write_bytes. { json configFile = validConfigFile; configFile["rules"][0]["actions"][1]["i2c_write_bytes"]["register"] = "0x82"; configFile["rules"][0]["actions"][1]["i2c_write_bytes"]["values"] = { "0x02", "0x73"}; configFile["rules"][0]["actions"][1]["i2c_write_bytes"]["masks"] = { "0x7F"}; EXPECT_JSON_INVALID(configFile, "Error: Invalid i2c_write_bytes action.", ""); } } TEST(ValidateRegulatorsConfigTest, CommandLineSyntax) { std::string validateTool = " ../phosphor-regulators/tools/validate-regulators-config.py "; std::string schema = " -s "; std::string schemaFile = " ../phosphor-regulators/schema/config_schema.json "; std::string configuration = " -c "; std::string command; std::string errorMessage; std::string outputMessage; std::string outputMessageHelp = "usage: validate-regulators-config.py [-h] [-s SCHEMA_FILE]"; int valid = 0; TemporaryFile tmpFile; std::string fileName = tmpFile.getPath().string(); writeDataToFile(validConfigFile, fileName); // Valid: -s specified { command = validateTool + "-s " + schemaFile + configuration + fileName; expectCommandLineSyntax(errorMessage, outputMessage, command, valid); } // Valid: --schema-file specified { command = validateTool + "--schema-file " + schemaFile + configuration + fileName; expectCommandLineSyntax(errorMessage, outputMessage, command, valid); } // Valid: -c specified { command = validateTool + schema + schemaFile + "-c " + fileName; expectCommandLineSyntax(errorMessage, outputMessage, command, valid); } // Valid: --configuration-file specified { command = validateTool + schema + schemaFile + "--configuration-file " + fileName; expectCommandLineSyntax(errorMessage, outputMessage, command, valid); } // Valid: -h specified { command = validateTool + "-h "; expectCommandLineSyntax(errorMessage, outputMessageHelp, command, valid); } // Valid: --help specified { command = validateTool + "--help "; expectCommandLineSyntax(errorMessage, outputMessageHelp, command, valid); } // Invalid: -c/--configuration-file not specified { command = validateTool + schema + schemaFile; expectCommandLineSyntax("Error: Configuration file is required.", outputMessageHelp, command, 1); } // Invalid: -s/--schema-file not specified { command = validateTool + configuration + fileName; expectCommandLineSyntax("Error: Schema file is required.", outputMessageHelp, command, 1); } // Invalid: -c specified more than once { command = validateTool + schema + schemaFile + "-c -c " + fileName; expectCommandLineSyntax(outputMessageHelp, outputMessage, command, 2); } // Invalid: -s specified more than once { command = validateTool + "-s -s " + schemaFile + configuration + fileName; expectCommandLineSyntax(outputMessageHelp, outputMessage, command, 2); } // Invalid: No file name specified after -c { command = validateTool + schema + schemaFile + configuration; expectCommandLineSyntax(outputMessageHelp, outputMessage, command, 2); } // Invalid: No file name specified after -s { command = validateTool + schema + configuration + fileName; expectCommandLineSyntax(outputMessageHelp, outputMessage, command, 2); } // Invalid: File specified after -c does not exist { command = validateTool + schema + schemaFile + configuration + "../notExistFile"; expectCommandLineSyntax("Error: Configuration file does not exist.", outputMessageHelp, command, 1); } // Invalid: File specified after -s does not exist { command = validateTool + schema + "../notExistFile " + configuration + fileName; expectCommandLineSyntax("Error: Schema file does not exist.", outputMessageHelp, command, 1); } // Invalid: File specified after -c is not right data format { TemporaryFile wrongFormatFile; std::string wrongFormatFileName = wrongFormatFile.getPath().string(); std::ofstream out(wrongFormatFileName); out << "foo"; out.close(); command = validateTool + schema + schemaFile + configuration + wrongFormatFileName; expectCommandLineSyntax( "Error: Configuration file is not in the JSON format.", outputMessageHelp, command, 1); } // Invalid: File specified after -s is not right data format { TemporaryFile wrongFormatFile; std::string wrongFormatFileName = wrongFormatFile.getPath().string(); std::ofstream out(wrongFormatFileName); out << "foo"; out.close(); command = validateTool + schema + wrongFormatFileName + configuration + fileName; expectCommandLineSyntax("Error: Schema file is not in the JSON format.", outputMessageHelp, command, 1); } // Invalid: File specified after -c is not readable { TemporaryFile notReadableFile; std::string notReadableFileName = notReadableFile.getPath().string(); writeDataToFile(validConfigFile, notReadableFileName); command = validateTool + schema + schemaFile + configuration + notReadableFileName; chmod(notReadableFileName.c_str(), 0222); expectCommandLineSyntax("Error: Configuration file is not readable.", outputMessageHelp, command, 1); } // Invalid: File specified after -s is not readable { TemporaryFile notReadableFile; std::string notReadableFileName = notReadableFile.getPath().string(); writeDataToFile(validConfigFile, notReadableFileName); command = validateTool + schema + notReadableFileName + configuration + fileName; chmod(notReadableFileName.c_str(), 0222); expectCommandLineSyntax("Error: Schema file is not readable.", outputMessageHelp, command, 1); } // Invalid: Unexpected parameter specified (like -g) { command = validateTool + schema + schemaFile + configuration + fileName + " -g"; expectCommandLineSyntax(outputMessageHelp, outputMessage, command, 2); } }