/** * Copyright © 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 "action.hpp" #include "and_action.hpp" #include "chassis.hpp" #include "compare_presence_action.hpp" #include "compare_vpd_action.hpp" #include "config_file_parser.hpp" #include "config_file_parser_error.hpp" #include "configuration.hpp" #include "device.hpp" #include "i2c_capture_bytes_action.hpp" #include "i2c_compare_bit_action.hpp" #include "i2c_compare_byte_action.hpp" #include "i2c_compare_bytes_action.hpp" #include "i2c_interface.hpp" #include "i2c_write_bit_action.hpp" #include "i2c_write_byte_action.hpp" #include "i2c_write_bytes_action.hpp" #include "log_phase_fault_action.hpp" #include "not_action.hpp" #include "or_action.hpp" #include "phase_fault.hpp" #include "phase_fault_detection.hpp" #include "pmbus_read_sensor_action.hpp" #include "pmbus_utils.hpp" #include "pmbus_write_vout_command_action.hpp" #include "presence_detection.hpp" #include "rail.hpp" #include "rule.hpp" #include "run_rule_action.hpp" #include "sensor_monitoring.hpp" #include "sensors.hpp" #include "set_device_action.hpp" #include "temporary_file.hpp" #include // for chmod() #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace phosphor::power::regulators; using namespace phosphor::power::regulators::config_file_parser; using namespace phosphor::power::regulators::config_file_parser::internal; using json = nlohmann::json; void writeConfigFile(const std::filesystem::path& pathName, const std::string& contents) { std::ofstream file{pathName}; file << contents; } void writeConfigFile(const std::filesystem::path& pathName, const json& contents) { std::ofstream file{pathName}; file << contents; } TEST(ConfigFileParserTests, Parse) { // Test where works { const json configFileContents = R"( { "rules": [ { "id": "set_voltage_rule1", "actions": [ { "pmbus_write_vout_command": { "volts": 1.03, "format": "linear" } } ] }, { "id": "set_voltage_rule2", "actions": [ { "pmbus_write_vout_command": { "volts": 1.33, "format": "linear" } } ] } ], "chassis": [ { "number": 1, "inventory_path": "system/chassis1" }, { "number": 2, "inventory_path": "system/chassis2" }, { "number": 3, "inventory_path": "system/chassis3" } ] } )"_json; TemporaryFile configFile; std::filesystem::path pathName{configFile.getPath()}; writeConfigFile(pathName, configFileContents); std::vector> rules{}; std::vector> chassis{}; std::tie(rules, chassis) = parse(pathName); EXPECT_EQ(rules.size(), 2); EXPECT_EQ(rules[0]->getID(), "set_voltage_rule1"); EXPECT_EQ(rules[1]->getID(), "set_voltage_rule2"); EXPECT_EQ(chassis.size(), 3); EXPECT_EQ(chassis[0]->getNumber(), 1); EXPECT_EQ(chassis[0]->getInventoryPath(), "/xyz/openbmc_project/inventory/system/chassis1"); EXPECT_EQ(chassis[1]->getNumber(), 2); EXPECT_EQ(chassis[1]->getInventoryPath(), "/xyz/openbmc_project/inventory/system/chassis2"); EXPECT_EQ(chassis[2]->getNumber(), 3); EXPECT_EQ(chassis[2]->getInventoryPath(), "/xyz/openbmc_project/inventory/system/chassis3"); } // Test where fails: File does not exist try { std::filesystem::path pathName{"/tmp/non_existent_file"}; parse(pathName); ADD_FAILURE() << "Should not have reached this line."; } catch (const ConfigFileParserError& e) { // Expected exception; what() message will vary } // Test where fails: File is not readable try { const json configFileContents = R"( { "chassis": [ { "number": 1, "inventory_path": "system/chassis1" } ] } )"_json; TemporaryFile configFile; std::filesystem::path pathName{configFile.getPath()}; writeConfigFile(pathName, configFileContents); chmod(pathName.c_str(), 0222); parse(pathName); ADD_FAILURE() << "Should not have reached this line."; } catch (const ConfigFileParserError& e) { // Expected exception; what() message will vary } // Test where fails: File is not valid JSON try { const std::string configFileContents = "] foo ["; TemporaryFile configFile; std::filesystem::path pathName{configFile.getPath()}; writeConfigFile(pathName, configFileContents); parse(pathName); ADD_FAILURE() << "Should not have reached this line."; } catch (const ConfigFileParserError& e) { // Expected exception; what() message will vary } // Test where fails: Error when parsing JSON elements try { const json configFileContents = R"( { "foo": "bar" } )"_json; TemporaryFile configFile; std::filesystem::path pathName{configFile.getPath()}; writeConfigFile(pathName, configFileContents); parse(pathName); ADD_FAILURE() << "Should not have reached this line."; } catch (const ConfigFileParserError& e) { // Expected exception; what() message will vary } } TEST(ConfigFileParserTests, GetRequiredProperty) { // Test where property exists { const json element = R"( { "format": "linear" } )"_json; const json& propertyElement = getRequiredProperty(element, "format"); EXPECT_EQ(propertyElement.get(), "linear"); } // Test where property does not exist try { const json element = R"( { "volts": 1.03 } )"_json; getRequiredProperty(element, "format"); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Required property missing: format"); } } TEST(ConfigFileParserTests, ParseAction) { // Test where works: comments property specified { const json element = R"( { "comments": [ "Set output voltage." ], "pmbus_write_vout_command": { "format": "linear" } } )"_json; std::unique_ptr action = parseAction(element); EXPECT_NE(action.get(), nullptr); } // Test where works: comments property not specified { const json element = R"( { "pmbus_write_vout_command": { "format": "linear" } } )"_json; std::unique_ptr action = parseAction(element); EXPECT_NE(action.get(), nullptr); } // Test where works: and action type specified { const json element = R"( { "and": [ { "i2c_compare_byte": { "register": "0xA0", "value": "0x00" } }, { "i2c_compare_byte": { "register": "0xA1", "value": "0x00" } } ] } )"_json; std::unique_ptr action = parseAction(element); EXPECT_NE(action.get(), nullptr); } // Test where works: compare_presence action type specified { const json element = R"( { "compare_presence": { "fru": "system/chassis/motherboard/cpu3", "value": true } } )"_json; std::unique_ptr action = parseAction(element); EXPECT_NE(action.get(), nullptr); } // Test where works: compare_vpd action type specified { const json element = R"( { "compare_vpd": { "fru": "system/chassis/disk_backplane", "keyword": "CCIN", "value": "2D35" } } )"_json; std::unique_ptr action = parseAction(element); EXPECT_NE(action.get(), nullptr); } // Test where works: i2c_capture_bytes action type specified { const json element = R"( { "i2c_capture_bytes": { "register": "0xA0", "count": 2 } } )"_json; std::unique_ptr action = parseAction(element); EXPECT_NE(action.get(), nullptr); } // Test where works: i2c_compare_bit action type specified { const json element = R"( { "i2c_compare_bit": { "register": "0xA0", "position": 3, "value": 0 } } )"_json; std::unique_ptr action = parseAction(element); EXPECT_NE(action.get(), nullptr); } // Test where works: i2c_compare_byte action type specified { const json element = R"( { "i2c_compare_byte": { "register": "0x0A", "value": "0xCC" } } )"_json; std::unique_ptr action = parseAction(element); EXPECT_NE(action.get(), nullptr); } // Test where works: i2c_compare_bytes action type specified { const json element = R"( { "i2c_compare_bytes": { "register": "0x0A", "values": [ "0xCC", "0xFF" ] } } )"_json; std::unique_ptr action = parseAction(element); EXPECT_NE(action.get(), nullptr); } // Test where works: i2c_write_bit action type specified { const json element = R"( { "i2c_write_bit": { "register": "0xA0", "position": 3, "value": 0 } } )"_json; std::unique_ptr action = parseAction(element); EXPECT_NE(action.get(), nullptr); } // Test where works: i2c_write_byte action type specified { const json element = R"( { "i2c_write_byte": { "register": "0x0A", "value": "0xCC" } } )"_json; std::unique_ptr action = parseAction(element); EXPECT_NE(action.get(), nullptr); } // Test where works: i2c_write_bytes action type specified { const json element = R"( { "i2c_write_bytes": { "register": "0x0A", "values": [ "0xCC", "0xFF" ] } } )"_json; std::unique_ptr action = parseAction(element); EXPECT_NE(action.get(), nullptr); } // Test where works: if action type specified { const json element = R"( { "if": { "condition": { "run_rule": "is_downlevel_regulator" }, "then": [ { "run_rule": "configure_downlevel_regulator" } ], "else": [ { "run_rule": "configure_standard_regulator" } ] } } )"_json; std::unique_ptr action = parseAction(element); EXPECT_NE(action.get(), nullptr); } // Test where works: log_phase_fault action type specified { const json element = R"( { "log_phase_fault": { "type": "n+1" } } )"_json; std::unique_ptr action = parseAction(element); EXPECT_NE(action.get(), nullptr); } // Test where works: not action type specified { const json element = R"( { "not": { "i2c_compare_byte": { "register": "0xA0", "value": "0xFF" } } } )"_json; std::unique_ptr action = parseAction(element); EXPECT_NE(action.get(), nullptr); } // Test where works: or action type specified { const json element = R"( { "or": [ { "i2c_compare_byte": { "register": "0xA0", "value": "0x00" } }, { "i2c_compare_byte": { "register": "0xA1", "value": "0x00" } } ] } )"_json; std::unique_ptr action = parseAction(element); EXPECT_NE(action.get(), nullptr); } // Test where works: pmbus_read_sensor action type specified { const json element = R"( { "pmbus_read_sensor": { "type": "iout", "command": "0x8C", "format": "linear_11" } } )"_json; std::unique_ptr action = parseAction(element); EXPECT_NE(action.get(), nullptr); } // Test where works: pmbus_write_vout_command action type specified { const json element = R"( { "pmbus_write_vout_command": { "format": "linear" } } )"_json; std::unique_ptr action = parseAction(element); EXPECT_NE(action.get(), nullptr); } // Test where works: run_rule action type specified { const json element = R"( { "run_rule": "set_voltage_rule" } )"_json; std::unique_ptr action = parseAction(element); EXPECT_NE(action.get(), nullptr); } // Test where works: set_device action type specified { const json element = R"( { "set_device": "io_expander2" } )"_json; std::unique_ptr action = parseAction(element); EXPECT_NE(action.get(), nullptr); } // Test where fails: Element is not an object try { const json element = R"( [ "0xFF", "0x01" ] )"_json; parseAction(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element is not an object"); } // Test where fails: No action type specified try { const json element = R"( { "comments": [ "Set output voltage." ] } )"_json; parseAction(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Required action type property missing"); } // Test where fails: Multiple action types specified try { const json element = R"( { "pmbus_write_vout_command": { "format": "linear" }, "run_rule": "set_voltage_rule" } )"_json; parseAction(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element contains an invalid property"); } // Test where fails: Invalid property specified try { const json element = R"( { "remarks": [ "Set output voltage." ], "pmbus_write_vout_command": { "format": "linear" } } )"_json; parseAction(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element contains an invalid property"); } } TEST(ConfigFileParserTests, ParseActionArray) { // Test where works { const json element = R"( [ { "pmbus_write_vout_command": { "volts": 1.01, "format": "linear" } }, { "pmbus_write_vout_command": { "volts": 1.03, "format": "linear" } } ] )"_json; std::vector> actions = parseActionArray(element); EXPECT_EQ(actions.size(), 2); } // Test where fails: Element is not an array try { const json element = R"( { "foo": "bar" } )"_json; parseActionArray(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element is not an array"); } } TEST(ConfigFileParserTests, ParseAnd) { // Test where works: Element is an array with 2 actions { const json element = R"( [ { "i2c_compare_byte": { "register": "0xA0", "value": "0x00" } }, { "i2c_compare_byte": { "register": "0xA1", "value": "0x00" } } ] )"_json; std::unique_ptr action = parseAnd(element); EXPECT_EQ(action->getActions().size(), 2); } // Test where fails: Element is an array with 1 action try { const json element = R"( [ { "i2c_compare_byte": { "register": "0xA0", "value": "0x00" } } ] )"_json; parseAnd(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Array must contain two or more actions"); } // Test where fails: Element is not an array try { const json element = R"( { "foo": "bar" } )"_json; parseAnd(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element is not an array"); } } TEST(ConfigFileParserTests, ParseBitPosition) { // Test where works: 0 { const json element = R"( 0 )"_json; uint8_t value = parseBitPosition(element); EXPECT_EQ(value, 0); } // Test where works: 7 { const json element = R"( 7 )"_json; uint8_t value = parseBitPosition(element); EXPECT_EQ(value, 7); } // Test where fails: Element is not an integer try { const json element = R"( 1.03 )"_json; parseBitPosition(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element is not an integer"); } // Test where fails: Value < 0 try { const json element = R"( -1 )"_json; parseBitPosition(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element is not a bit position"); } // Test where fails: Value > 7 try { const json element = R"( 8 )"_json; parseBitPosition(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element is not a bit position"); } } TEST(ConfigFileParserTests, ParseBitValue) { // Test where works: 0 { const json element = R"( 0 )"_json; uint8_t value = parseBitValue(element); EXPECT_EQ(value, 0); } // Test where works: 1 { const json element = R"( 1 )"_json; uint8_t value = parseBitValue(element); EXPECT_EQ(value, 1); } // Test where fails: Element is not an integer try { const json element = R"( 0.5 )"_json; parseBitValue(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element is not an integer"); } // Test where fails: Value < 0 try { const json element = R"( -1 )"_json; parseBitValue(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element is not a bit value"); } // Test where fails: Value > 1 try { const json element = R"( 2 )"_json; parseBitValue(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element is not a bit value"); } } TEST(ConfigFileParserTests, ParseBoolean) { // Test where works: true { const json element = R"( true )"_json; bool value = parseBoolean(element); EXPECT_EQ(value, true); } // Test where works: false { const json element = R"( false )"_json; bool value = parseBoolean(element); EXPECT_EQ(value, false); } // Test where fails: Element is not a boolean try { const json element = R"( 1 )"_json; parseBoolean(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element is not a boolean"); } } TEST(ConfigFileParserTests, ParseChassis) { // Test where works: Only required properties specified { const json element = R"( { "number": 1, "inventory_path": "system/chassis1" } )"_json; std::unique_ptr chassis = parseChassis(element); EXPECT_EQ(chassis->getNumber(), 1); EXPECT_EQ(chassis->getInventoryPath(), "/xyz/openbmc_project/inventory/system/chassis1"); EXPECT_EQ(chassis->getDevices().size(), 0); } // Test where works: All properties specified { const json element = R"( { "comments": [ "comments property" ], "number": 2, "inventory_path": "system/chassis2", "devices": [ { "id": "vdd_regulator", "is_regulator": true, "fru": "system/chassis/motherboard/regulator2", "i2c_interface": { "bus": 1, "address": "0x70" } } ] } )"_json; std::unique_ptr chassis = parseChassis(element); EXPECT_EQ(chassis->getNumber(), 2); EXPECT_EQ(chassis->getInventoryPath(), "/xyz/openbmc_project/inventory/system/chassis2"); EXPECT_EQ(chassis->getDevices().size(), 1); EXPECT_EQ(chassis->getDevices()[0]->getID(), "vdd_regulator"); } // Test where fails: number value is invalid try { const json element = R"( { "number": 0.5, "inventory_path": "system/chassis" } )"_json; parseChassis(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element is not an unsigned integer"); } // Test where fails: inventory_path is invalid: Not a string try { const json element = R"( { "number": 2, "inventory_path": true } )"_json; parseChassis(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element is not a string"); } // Test where fails: inventory_path is invalid: Empty string try { const json element = R"( { "number": 2, "inventory_path": "" } )"_json; parseChassis(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element contains an empty string"); } // Test where fails: Invalid property specified try { const json element = R"( { "number": 1, "inventory_path": "system/chassis", "foo": 2 } )"_json; parseChassis(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element contains an invalid property"); } // Test where fails: Required number property not specified try { const json element = R"( { "inventory_path": "system/chassis" } )"_json; parseChassis(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Required property missing: number"); } // Test where fails: Required inventory_path property not specified try { const json element = R"( { "number": 1 } )"_json; parseChassis(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Required property missing: inventory_path"); } // Test where fails: Element is not an object try { const json element = R"( [ "0xFF", "0x01" ] )"_json; parseChassis(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element is not an object"); } // Test where fails: number value is < 1 try { const json element = R"( { "number": 0, "inventory_path": "system/chassis" } )"_json; parseChassis(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Invalid chassis number: Must be > 0"); } // Test where fails: devices value is invalid try { const json element = R"( { "number": 1, "inventory_path": "system/chassis", "devices": 2 } )"_json; parseChassis(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element is not an array"); } } TEST(ConfigFileParserTests, ParseChassisArray) { // Test where works { const json element = R"( [ { "number": 1, "inventory_path": "system/chassis1" }, { "number": 2, "inventory_path": "system/chassis2" } ] )"_json; std::vector> chassis = parseChassisArray(element); EXPECT_EQ(chassis.size(), 2); EXPECT_EQ(chassis[0]->getNumber(), 1); EXPECT_EQ(chassis[0]->getInventoryPath(), "/xyz/openbmc_project/inventory/system/chassis1"); EXPECT_EQ(chassis[1]->getNumber(), 2); EXPECT_EQ(chassis[1]->getInventoryPath(), "/xyz/openbmc_project/inventory/system/chassis2"); } // Test where fails: Element is not an array try { const json element = R"( { "foo": "bar" } )"_json; parseChassisArray(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element is not an array"); } } TEST(ConfigFileParserTests, ParseComparePresence) { // Test where works { const json element = R"( { "fru": "system/chassis/motherboard/cpu3", "value": true } )"_json; std::unique_ptr action = parseComparePresence(element); EXPECT_EQ( action->getFRU(), "/xyz/openbmc_project/inventory/system/chassis/motherboard/cpu3"); EXPECT_EQ(action->getValue(), true); } // Test where fails: Element is not an object try { const json element = R"( [ "0xFF", "0x01" ] )"_json; parseComparePresence(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element is not an object"); } // Test where fails: Invalid property specified try { const json element = R"( { "fru": "system/chassis/motherboard/cpu3", "value": true, "foo" : true } )"_json; parseComparePresence(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element contains an invalid property"); } // Test where fails: Required fru property not specified try { const json element = R"( { "value": true } )"_json; parseComparePresence(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Required property missing: fru"); } // Test where fails: Required value property not specified try { const json element = R"( { "fru": "system/chassis/motherboard/cpu3" } )"_json; parseComparePresence(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Required property missing: value"); } // Test where fails: fru value is invalid try { const json element = R"( { "fru": 1, "value": true } )"_json; parseComparePresence(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element is not a string"); } // Test where fails: value value is invalid try { const json element = R"( { "fru": "system/chassis/motherboard/cpu3", "value": 1 } )"_json; parseComparePresence(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element is not a boolean"); } } TEST(ConfigFileParserTests, ParseCompareVPD) { // Test where works: value property: Not empty { const json element = R"( { "fru": "system/chassis/disk_backplane", "keyword": "CCIN", "value": "2D35" } )"_json; std::unique_ptr action = parseCompareVPD(element); EXPECT_EQ( action->getFRU(), "/xyz/openbmc_project/inventory/system/chassis/disk_backplane"); EXPECT_EQ(action->getKeyword(), "CCIN"); EXPECT_EQ(action->getValue(), (std::vector{0x32, 0x44, 0x33, 0x35})); } // Test where works: value property: Empty { const json element = R"( { "fru": "system/chassis/disk_backplane", "keyword": "CCIN", "value": "" } )"_json; std::unique_ptr action = parseCompareVPD(element); EXPECT_EQ( action->getFRU(), "/xyz/openbmc_project/inventory/system/chassis/disk_backplane"); EXPECT_EQ(action->getKeyword(), "CCIN"); EXPECT_EQ(action->getValue(), (std::vector{})); } // Test where works: byte_values property: Not empty { const json element = R"( { "fru": "system/chassis/disk_backplane", "keyword": "CCIN", "byte_values": ["0x11", "0x22", "0x33"] } )"_json; std::unique_ptr action = parseCompareVPD(element); EXPECT_EQ( action->getFRU(), "/xyz/openbmc_project/inventory/system/chassis/disk_backplane"); EXPECT_EQ(action->getKeyword(), "CCIN"); EXPECT_EQ(action->getValue(), (std::vector{0x11, 0x22, 0x33})); } // Test where works: byte_values property: Empty { const json element = R"( { "fru": "system/chassis/disk_backplane", "keyword": "CCIN", "byte_values": [] } )"_json; std::unique_ptr action = parseCompareVPD(element); EXPECT_EQ( action->getFRU(), "/xyz/openbmc_project/inventory/system/chassis/disk_backplane"); EXPECT_EQ(action->getKeyword(), "CCIN"); EXPECT_EQ(action->getValue(), (std::vector{})); } // Test where fails: Element is not an object try { const json element = R"( [ "0xFF", "0x01" ] )"_json; parseCompareVPD(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element is not an object"); } // Test where fails: Invalid property specified try { const json element = R"( { "fru": "system/chassis/disk_backplane", "keyword": "CCIN", "value": "2D35", "foo" : true } )"_json; parseCompareVPD(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element contains an invalid property"); } // Test where fails: Required fru property not specified try { const json element = R"( { "keyword": "CCIN", "value": "2D35" } )"_json; parseCompareVPD(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Required property missing: fru"); } // Test where fails: Required keyword property not specified try { const json element = R"( { "fru": "system/chassis/disk_backplane", "value": "2D35" } )"_json; parseCompareVPD(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Required property missing: keyword"); } // Test where fails: Required value property not specified try { const json element = R"( { "fru": "system/chassis/disk_backplane", "keyword": "CCIN" } )"_json; parseCompareVPD(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Invalid property: Must contain " "either value or byte_values"); } // Test where fails: both value and byte_values specified try { const json element = R"( { "fru": "system/chassis/disk_backplane", "keyword": "CCIN", "value": "2D35", "byte_values": [ "0x01", "0x02" ] } )"_json; parseCompareVPD(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Invalid property: Must contain " "either value or byte_values"); } // Test where fails: fru value is invalid try { const json element = R"( { "fru": 1, "keyword": "CCIN", "value": "2D35" } )"_json; parseCompareVPD(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element is not a string"); } // Test where fails: keyword value is invalid try { const json element = R"( { "fru": "system/chassis/disk_backplane", "keyword": 1, "value": "2D35" } )"_json; parseCompareVPD(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element is not a string"); } // Test where fails: value value is invalid try { const json element = R"( { "fru": "system/chassis/disk_backplane", "keyword": "CCIN", "value": 1 } )"_json; parseCompareVPD(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element is not a string"); } // Test where fails: byte_values is wrong format try { const json element = R"( { "fru": "system/chassis/disk_backplane", "keyword": "CCIN", "byte_values": [1, 2, 3] } )"_json; parseCompareVPD(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element is not a string"); } } TEST(ConfigFileParserTests, ParseConfiguration) { // Test where works: actions required property specified { const json element = R"( { "actions": [ { "pmbus_write_vout_command": { "format": "linear" } } ] } )"_json; std::unique_ptr configuration = parseConfiguration(element); EXPECT_EQ(configuration->getActions().size(), 1); EXPECT_EQ(configuration->getVolts().has_value(), false); } // Test where works: volts and actions properties specified { const json element = R"( { "comments": [ "comments property" ], "volts": 1.03, "actions": [ { "pmbus_write_vout_command": { "format": "linear" } }, { "run_rule": "set_voltage_rule" } ] } )"_json; std::unique_ptr configuration = parseConfiguration(element); EXPECT_EQ(configuration->getVolts().has_value(), true); EXPECT_EQ(configuration->getVolts().value(), 1.03); EXPECT_EQ(configuration->getActions().size(), 2); } // Test where works: volts and rule_id properties specified { const json element = R"( { "volts": 1.05, "rule_id": "set_voltage_rule" } )"_json; std::unique_ptr configuration = parseConfiguration(element); EXPECT_EQ(configuration->getVolts().has_value(), true); EXPECT_EQ(configuration->getVolts().value(), 1.05); EXPECT_EQ(configuration->getActions().size(), 1); } // Test where fails: volts value is invalid try { const json element = R"( { "volts": "foo", "actions": [ { "pmbus_write_vout_command": { "format": "linear" } } ] } )"_json; parseConfiguration(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element is not a number"); } // Test where fails: actions object is invalid try { const json element = R"( { "volts": 1.03, "actions": 1 } )"_json; parseConfiguration(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element is not an array"); } // Test where fails: rule_id value is invalid try { const json element = R"( { "volts": 1.05, "rule_id": 1 } )"_json; parseConfiguration(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element is not a string"); } // Test where fails: Required actions or rule_id property not specified try { const json element = R"( { "volts": 1.03 } )"_json; parseConfiguration(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Invalid property combination: Must contain " "either rule_id or actions"); } // Test where fails: Required actions or rule_id property both specified try { const json element = R"( { "volts": 1.03, "rule_id": "set_voltage_rule", "actions": [ { "pmbus_write_vout_command": { "format": "linear" } } ] } )"_json; parseConfiguration(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Invalid property combination: Must contain " "either rule_id or actions"); } // Test where fails: Element is not an object try { const json element = R"( [ "0xFF", "0x01" ] )"_json; parseConfiguration(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element is not an object"); } // Test where fails: Invalid property specified try { const json element = R"( { "volts": 1.03, "rule_id": "set_voltage_rule", "foo": 1 } )"_json; parseConfiguration(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element contains an invalid property"); } } TEST(ConfigFileParserTests, ParseDevice) { // Test where works: Only required properties specified { const json element = R"( { "id": "vdd_regulator", "is_regulator": true, "fru": "system/chassis/motherboard/regulator2", "i2c_interface": { "bus": 1, "address": "0x70" } } )"_json; std::unique_ptr device = parseDevice(element); EXPECT_EQ(device->getID(), "vdd_regulator"); EXPECT_EQ(device->isRegulator(), true); EXPECT_EQ(device->getFRU(), "/xyz/openbmc_project/inventory/system/" "chassis/motherboard/regulator2"); EXPECT_NE(&(device->getI2CInterface()), nullptr); EXPECT_EQ(device->getPresenceDetection(), nullptr); EXPECT_EQ(device->getConfiguration(), nullptr); EXPECT_EQ(device->getPhaseFaultDetection(), nullptr); EXPECT_EQ(device->getRails().size(), 0); } // Test where works: All properties specified { const json element = R"( { "comments": [ "VDD Regulator" ], "id": "vdd_regulator", "is_regulator": true, "fru": "system/chassis/motherboard/regulator2", "i2c_interface": { "bus": 1, "address": "0x70" }, "presence_detection": { "rule_id": "is_foobar_backplane_installed_rule" }, "configuration": { "rule_id": "configure_ir35221_rule" }, "phase_fault_detection": { "rule_id": "detect_phase_fault_rule" }, "rails": [ { "id": "vdd" } ] } )"_json; std::unique_ptr device = parseDevice(element); EXPECT_EQ(device->getID(), "vdd_regulator"); EXPECT_EQ(device->isRegulator(), true); EXPECT_EQ(device->getFRU(), "/xyz/openbmc_project/inventory/system/" "chassis/motherboard/regulator2"); EXPECT_NE(&(device->getI2CInterface()), nullptr); EXPECT_NE(device->getPresenceDetection(), nullptr); EXPECT_NE(device->getConfiguration(), nullptr); EXPECT_NE(device->getPhaseFaultDetection(), nullptr); EXPECT_EQ(device->getRails().size(), 1); } // Test where fails: phase_fault_detection property exists and is_regulator // is false try { const json element = R"( { "id": "vdd_regulator", "is_regulator": false, "fru": "system/chassis/motherboard/regulator2", "i2c_interface": { "bus": 1, "address": "0x70" }, "phase_fault_detection": { "rule_id": "detect_phase_fault_rule" } } )"_json; parseDevice(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Invalid phase_fault_detection property when " "is_regulator is false"); } // Test where fails: rails property exists and is_regulator is false try { const json element = R"( { "id": "vdd_regulator", "is_regulator": false, "fru": "system/chassis/motherboard/regulator2", "i2c_interface": { "bus": 1, "address": "0x70" }, "configuration": { "rule_id": "configure_ir35221_rule" }, "rails": [ { "id": "vdd" } ] } )"_json; parseDevice(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Invalid rails property when is_regulator is false"); } // Test where fails: id value is invalid try { const json element = R"( { "id": 3, "is_regulator": true, "fru": "system/chassis/motherboard/regulator2", "i2c_interface": { "bus": 1, "address": "0x70" } } )"_json; parseDevice(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element is not a string"); } // Test where fails: is_regulator value is invalid try { const json element = R"( { "id": "vdd_regulator", "is_regulator": 3, "fru": "system/chassis/motherboard/regulator2", "i2c_interface": { "bus": 1, "address": "0x70" } } )"_json; parseDevice(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element is not a boolean"); } // Test where fails: fru value is invalid try { const json element = R"( { "id": "vdd_regulator", "is_regulator": true, "fru": 2, "i2c_interface": { "bus": 1, "address": "0x70" } } )"_json; parseDevice(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element is not a string"); } // Test where fails: i2c_interface value is invalid try { const json element = R"( { "id": "vdd_regulator", "is_regulator": true, "fru": "system/chassis/motherboard/regulator2", "i2c_interface": 3 } )"_json; parseDevice(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element is not an object"); } // Test where fails: Required id property not specified try { const json element = R"( { "is_regulator": true, "fru": "system/chassis/motherboard/regulator2", "i2c_interface": { "bus": 1, "address": "0x70" } } )"_json; parseDevice(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Required property missing: id"); } // Test where fails: Required is_regulator property not specified try { const json element = R"( { "id": "vdd_regulator", "fru": "system/chassis/motherboard/regulator2", "i2c_interface": { "bus": 1, "address": "0x70" } } )"_json; parseDevice(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Required property missing: is_regulator"); } // Test where fails: Required fru property not specified try { const json element = R"( { "id": "vdd_regulator", "is_regulator": true, "i2c_interface": { "bus": 1, "address": "0x70" } } )"_json; parseDevice(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Required property missing: fru"); } // Test where fails: Required i2c_interface property not specified try { const json element = R"( { "id": "vdd_regulator", "is_regulator": true, "fru": "system/chassis/motherboard/regulator2" } )"_json; parseDevice(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Required property missing: i2c_interface"); } // Test where fails: Element is not an object try { const json element = R"( [ "0xFF", "0x01" ] )"_json; parseDevice(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element is not an object"); } // Test where fails: Invalid property specified try { const json element = R"( { "id": "vdd_regulator", "is_regulator": true, "fru": "system/chassis/motherboard/regulator2", "i2c_interface": { "bus": 1, "address": "0x70" }, "foo" : true } )"_json; parseDevice(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element contains an invalid property"); } } TEST(ConfigFileParserTests, ParseDeviceArray) { // Test where works { const json element = R"( [ { "id": "vdd_regulator", "is_regulator": true, "fru": "system/chassis/motherboard/regulator2", "i2c_interface": { "bus": 1, "address": "0x70" } }, { "id": "vio_regulator", "is_regulator": true, "fru": "system/chassis/motherboard/regulator2", "i2c_interface": { "bus": 1, "address": "0x71" } } ] )"_json; std::vector> devices = parseDeviceArray(element); EXPECT_EQ(devices.size(), 2); EXPECT_EQ(devices[0]->getID(), "vdd_regulator"); EXPECT_EQ(devices[1]->getID(), "vio_regulator"); } // Test where fails: Element is not an array try { const json element = R"( { "foo": "bar" } )"_json; parseDeviceArray(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element is not an array"); } } TEST(ConfigFileParserTests, ParseDouble) { // Test where works: floating point value { const json element = R"( 1.03 )"_json; double value = parseDouble(element); EXPECT_EQ(value, 1.03); } // Test where works: integer value { const json element = R"( 24 )"_json; double value = parseDouble(element); EXPECT_EQ(value, 24.0); } // Test where fails: Element is not a number try { const json element = R"( true )"_json; parseDouble(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element is not a number"); } } TEST(ConfigFileParserTests, ParseHexByte) { // Test where works: "0xFF" { const json element = R"( "0xFF" )"_json; uint8_t value = parseHexByte(element); EXPECT_EQ(value, 0xFF); } // Test where works: "0xff" { const json element = R"( "0xff" )"_json; uint8_t value = parseHexByte(element); EXPECT_EQ(value, 0xff); } // Test where works: "0xf" { const json element = R"( "0xf" )"_json; uint8_t value = parseHexByte(element); EXPECT_EQ(value, 0xf); } // Test where fails: "0xfff" try { const json element = R"( "0xfff" )"_json; parseHexByte(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element is not hexadecimal string"); } // Test where fails: "0xAG" try { const json element = R"( "0xAG" )"_json; parseHexByte(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element is not hexadecimal string"); } // Test where fails: "ff" try { const json element = R"( "ff" )"_json; parseHexByte(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element is not hexadecimal string"); } // Test where fails: "" try { const json element = ""; parseHexByte(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element is not hexadecimal string"); } // Test where fails: "f" try { const json element = R"( "f" )"_json; parseHexByte(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element is not hexadecimal string"); } // Test where fails: "0x" try { const json element = R"( "0x" )"_json; parseHexByte(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element is not hexadecimal string"); } // Test where fails: "0Xff" try { const json element = R"( "0XFF" )"_json; parseHexByte(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element is not hexadecimal string"); } } TEST(ConfigFileParserTests, ParseHexByteArray) { // Test where works { const json element = R"( [ "0xCC", "0xFF" ] )"_json; std::vector hexBytes = parseHexByteArray(element); std::vector expected = {0xcc, 0xff}; EXPECT_EQ(hexBytes, expected); } // Test where fails: Element is not an array try { const json element = 0; parseHexByteArray(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element is not an array"); } } TEST(ConfigFileParserTests, ParseI2CCaptureBytes) { // Test where works { const json element = R"( { "register": "0xA0", "count": 2 } )"_json; std::unique_ptr action = parseI2CCaptureBytes(element); EXPECT_EQ(action->getRegister(), 0xA0); EXPECT_EQ(action->getCount(), 2); } // Test where fails: Element is not an object try { const json element = R"( [ "0xFF", "0x01" ] )"_json; parseI2CCaptureBytes(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element is not an object"); } // Test where fails: register value is invalid try { const json element = R"( { "register": "0x0Z", "count": 2 } )"_json; parseI2CCaptureBytes(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element is not hexadecimal string"); } // Test where fails: count value is invalid try { const json element = R"( { "register": "0xA0", "count": 0 } )"_json; parseI2CCaptureBytes(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Invalid byte count: Must be > 0"); } // Test where fails: Required register property not specified try { const json element = R"( { "count": 2 } )"_json; parseI2CCaptureBytes(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Required property missing: register"); } // Test where fails: Required count property not specified try { const json element = R"( { "register": "0xA0" } )"_json; parseI2CCaptureBytes(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Required property missing: count"); } // Test where fails: Invalid property specified try { const json element = R"( { "register": "0xA0", "count": 2, "foo": 3 } )"_json; parseI2CCaptureBytes(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element contains an invalid property"); } } TEST(ConfigFileParserTests, ParseI2CCompareBit) { // Test where works { const json element = R"( { "register": "0xA0", "position": 3, "value": 0 } )"_json; std::unique_ptr action = parseI2CCompareBit(element); EXPECT_EQ(action->getRegister(), 0xA0); EXPECT_EQ(action->getPosition(), 3); EXPECT_EQ(action->getValue(), 0); } // Test where fails: Invalid property specified try { const json element = R"( { "register": "0xA0", "position": 3, "value": 0, "foo": 3 } )"_json; parseI2CCompareBit(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element contains an invalid property"); } // Test where fails: Element is not an object try { const json element = R"( [ "0xFF", "0x01" ] )"_json; parseI2CCompareBit(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element is not an object"); } // Test where fails: register value is invalid try { const json element = R"( { "register": "0xAG", "position": 3, "value": 0 } )"_json; parseI2CCompareBit(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element is not hexadecimal string"); } // Test where fails: position value is invalid try { const json element = R"( { "register": "0xA0", "position": 8, "value": 0 } )"_json; parseI2CCompareBit(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element is not a bit position"); } // Test where fails: value value is invalid try { const json element = R"( { "register": "0xA0", "position": 3, "value": 2 } )"_json; parseI2CCompareBit(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element is not a bit value"); } // Test where fails: Required register property not specified try { const json element = R"( { "position": 3, "value": 0 } )"_json; parseI2CCompareBit(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Required property missing: register"); } // Test where fails: Required position property not specified try { const json element = R"( { "register": "0xA0", "value": 0 } )"_json; parseI2CCompareBit(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Required property missing: position"); } // Test where fails: Required value property not specified try { const json element = R"( { "register": "0xA0", "position": 3 } )"_json; parseI2CCompareBit(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Required property missing: value"); } } TEST(ConfigFileParserTests, ParseI2CCompareByte) { // Test where works: Only required properties specified { const json element = R"( { "register": "0x0A", "value": "0xCC" } )"_json; std::unique_ptr action = parseI2CCompareByte(element); EXPECT_EQ(action->getRegister(), 0x0A); EXPECT_EQ(action->getValue(), 0xCC); EXPECT_EQ(action->getMask(), 0xFF); } // Test where works: All properties specified { const json element = R"( { "register": "0x0A", "value": "0xCC", "mask": "0xF7" } )"_json; std::unique_ptr action = parseI2CCompareByte(element); EXPECT_EQ(action->getRegister(), 0x0A); EXPECT_EQ(action->getValue(), 0xCC); EXPECT_EQ(action->getMask(), 0xF7); } // Test where fails: Element is not an object try { const json element = R"( [ "0xFF", "0x01" ] )"_json; parseI2CCompareByte(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element is not an object"); } // Test where fails: Invalid property specified try { const json element = R"( { "register": "0x0A", "value": "0xCC", "mask": "0xF7", "foo": 1 } )"_json; parseI2CCompareByte(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element contains an invalid property"); } // Test where fails: register value is invalid try { const json element = R"( { "register": "0x0Z", "value": "0xCC", "mask": "0xF7" } )"_json; parseI2CCompareByte(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element is not hexadecimal string"); } // Test where fails: value value is invalid try { const json element = R"( { "register": "0x0A", "value": "0xCCC", "mask": "0xF7" } )"_json; parseI2CCompareByte(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element is not hexadecimal string"); } // Test where fails: mask value is invalid try { const json element = R"( { "register": "0x0A", "value": "0xCC", "mask": "F7" } )"_json; parseI2CCompareByte(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element is not hexadecimal string"); } // Test where fails: Required register property not specified try { const json element = R"( { "value": "0xCC", "mask": "0xF7" } )"_json; parseI2CCompareByte(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Required property missing: register"); } // Test where fails: Required value property not specified try { const json element = R"( { "register": "0x0A", "mask": "0xF7" } )"_json; parseI2CCompareByte(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Required property missing: value"); } } TEST(ConfigFileParserTests, ParseI2CCompareBytes) { // Test where works: Only required properties specified { const json element = R"( { "register": "0x0A", "values": [ "0xCC", "0xFF" ] } )"_json; std::unique_ptr action = parseI2CCompareBytes(element); EXPECT_EQ(action->getRegister(), 0x0A); EXPECT_EQ(action->getValues().size(), 2); EXPECT_EQ(action->getValues()[0], 0xCC); EXPECT_EQ(action->getValues()[1], 0xFF); EXPECT_EQ(action->getMasks().size(), 2); EXPECT_EQ(action->getMasks()[0], 0xFF); EXPECT_EQ(action->getMasks()[1], 0xFF); } // Test where works: All properties specified { const json element = R"( { "register": "0x0A", "values": [ "0xCC", "0xFF" ], "masks": [ "0x7F", "0x77" ] } )"_json; std::unique_ptr action = parseI2CCompareBytes(element); EXPECT_EQ(action->getRegister(), 0x0A); EXPECT_EQ(action->getValues().size(), 2); EXPECT_EQ(action->getValues()[0], 0xCC); EXPECT_EQ(action->getValues()[1], 0xFF); EXPECT_EQ(action->getMasks().size(), 2); EXPECT_EQ(action->getMasks()[0], 0x7F); EXPECT_EQ(action->getMasks()[1], 0x77); } // Test where fails: Element is not an object try { const json element = R"( [ "0xFF", "0x01" ] )"_json; parseI2CCompareBytes(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element is not an object"); } // Test where fails: Invalid property specified try { const json element = R"( { "register": "0x0A", "values": [ "0xCC", "0xFF" ], "masks": [ "0x7F", "0x7F" ], "foo": 1 } )"_json; parseI2CCompareBytes(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element contains an invalid property"); } // Test where fails: register value is invalid try { const json element = R"( { "register": "0x0Z", "values": [ "0xCC", "0xFF" ], "masks": [ "0x7F", "0x7F" ] } )"_json; parseI2CCompareBytes(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element is not hexadecimal string"); } // Test where fails: values value is invalid try { const json element = R"( { "register": "0x0A", "values": [ "0xCCC", "0xFF" ], "masks": [ "0x7F", "0x7F" ] } )"_json; parseI2CCompareBytes(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element is not hexadecimal string"); } // Test where fails: masks value is invalid try { const json element = R"( { "register": "0x0A", "values": [ "0xCC", "0xFF" ], "masks": [ "F", "0x7F" ] } )"_json; parseI2CCompareBytes(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element is not hexadecimal string"); } // Test where fails: number of elements in masks is invalid try { const json element = R"( { "register": "0x0A", "values": [ "0xCC", "0xFF" ], "masks": [ "0x7F" ] } )"_json; parseI2CCompareBytes(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Invalid number of elements in masks"); } // Test where fails: Required register property not specified try { const json element = R"( { "values": [ "0xCC", "0xFF" ] } )"_json; parseI2CCompareBytes(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Required property missing: register"); } // Test where fails: Required values property not specified try { const json element = R"( { "register": "0x0A" } )"_json; parseI2CCompareBytes(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Required property missing: values"); } } TEST(ConfigFileParserTests, ParseI2CWriteBit) { // Test where works { const json element = R"( { "register": "0xA0", "position": 3, "value": 0 } )"_json; std::unique_ptr action = parseI2CWriteBit(element); EXPECT_EQ(action->getRegister(), 0xA0); EXPECT_EQ(action->getPosition(), 3); EXPECT_EQ(action->getValue(), 0); } // Test where fails: Invalid property specified try { const json element = R"( { "register": "0xA0", "position": 3, "value": 0, "foo": 3 } )"_json; parseI2CWriteBit(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element contains an invalid property"); } // Test where fails: Element is not an object try { const json element = R"( [ "0xFF", "0x01" ] )"_json; parseI2CWriteBit(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element is not an object"); } // Test where fails: register value is invalid try { const json element = R"( { "register": "0xAG", "position": 3, "value": 0 } )"_json; parseI2CWriteBit(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element is not hexadecimal string"); } // Test where fails: position value is invalid try { const json element = R"( { "register": "0xA0", "position": 8, "value": 0 } )"_json; parseI2CWriteBit(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element is not a bit position"); } // Test where fails: value value is invalid try { const json element = R"( { "register": "0xA0", "position": 3, "value": 2 } )"_json; parseI2CWriteBit(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element is not a bit value"); } // Test where fails: Required register property not specified try { const json element = R"( { "position": 3, "value": 0 } )"_json; parseI2CWriteBit(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Required property missing: register"); } // Test where fails: Required position property not specified try { const json element = R"( { "register": "0xA0", "value": 0 } )"_json; parseI2CWriteBit(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Required property missing: position"); } // Test where fails: Required value property not specified try { const json element = R"( { "register": "0xA0", "position": 3 } )"_json; parseI2CWriteBit(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Required property missing: value"); } } TEST(ConfigFileParserTests, ParseI2CWriteByte) { // Test where works: Only required properties specified { const json element = R"( { "register": "0x0A", "value": "0xCC" } )"_json; std::unique_ptr action = parseI2CWriteByte(element); EXPECT_EQ(action->getRegister(), 0x0A); EXPECT_EQ(action->getValue(), 0xCC); EXPECT_EQ(action->getMask(), 0xFF); } // Test where works: All properties specified { const json element = R"( { "register": "0x0A", "value": "0xCC", "mask": "0xF7" } )"_json; std::unique_ptr action = parseI2CWriteByte(element); EXPECT_EQ(action->getRegister(), 0x0A); EXPECT_EQ(action->getValue(), 0xCC); EXPECT_EQ(action->getMask(), 0xF7); } // Test where fails: Element is not an object try { const json element = R"( [ "0xFF", "0x01" ] )"_json; parseI2CWriteByte(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element is not an object"); } // Test where fails: Invalid property specified try { const json element = R"( { "register": "0x0A", "value": "0xCC", "mask": "0xF7", "foo": 1 } )"_json; parseI2CWriteByte(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element contains an invalid property"); } // Test where fails: register value is invalid try { const json element = R"( { "register": "0x0Z", "value": "0xCC", "mask": "0xF7" } )"_json; parseI2CWriteByte(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element is not hexadecimal string"); } // Test where fails: value value is invalid try { const json element = R"( { "register": "0x0A", "value": "0xCCC", "mask": "0xF7" } )"_json; parseI2CWriteByte(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element is not hexadecimal string"); } // Test where fails: mask value is invalid try { const json element = R"( { "register": "0x0A", "value": "0xCC", "mask": "F7" } )"_json; parseI2CWriteByte(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element is not hexadecimal string"); } // Test where fails: Required register property not specified try { const json element = R"( { "value": "0xCC", "mask": "0xF7" } )"_json; parseI2CWriteByte(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Required property missing: register"); } // Test where fails: Required value property not specified try { const json element = R"( { "register": "0x0A", "mask": "0xF7" } )"_json; parseI2CWriteByte(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Required property missing: value"); } } TEST(ConfigFileParserTests, ParseI2CWriteBytes) { // Test where works: Only required properties specified { const json element = R"( { "register": "0x0A", "values": [ "0xCC", "0xFF" ] } )"_json; std::unique_ptr action = parseI2CWriteBytes(element); EXPECT_EQ(action->getRegister(), 0x0A); EXPECT_EQ(action->getValues().size(), 2); EXPECT_EQ(action->getValues()[0], 0xCC); EXPECT_EQ(action->getValues()[1], 0xFF); EXPECT_EQ(action->getMasks().size(), 0); } // Test where works: All properties specified { const json element = R"( { "register": "0x0A", "values": [ "0xCC", "0xFF" ], "masks": [ "0x7F", "0x77" ] } )"_json; std::unique_ptr action = parseI2CWriteBytes(element); EXPECT_EQ(action->getRegister(), 0x0A); EXPECT_EQ(action->getValues().size(), 2); EXPECT_EQ(action->getValues()[0], 0xCC); EXPECT_EQ(action->getValues()[1], 0xFF); EXPECT_EQ(action->getMasks().size(), 2); EXPECT_EQ(action->getMasks()[0], 0x7F); EXPECT_EQ(action->getMasks()[1], 0x77); } // Test where fails: Element is not an object try { const json element = R"( [ "0xFF", "0x01" ] )"_json; parseI2CWriteBytes(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element is not an object"); } // Test where fails: Invalid property specified try { const json element = R"( { "register": "0x0A", "values": [ "0xCC", "0xFF" ], "masks": [ "0x7F", "0x7F" ], "foo": 1 } )"_json; parseI2CWriteBytes(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element contains an invalid property"); } // Test where fails: register value is invalid try { const json element = R"( { "register": "0x0Z", "values": [ "0xCC", "0xFF" ], "masks": [ "0x7F", "0x7F" ] } )"_json; parseI2CWriteBytes(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element is not hexadecimal string"); } // Test where fails: values value is invalid try { const json element = R"( { "register": "0x0A", "values": [ "0xCCC", "0xFF" ], "masks": [ "0x7F", "0x7F" ] } )"_json; parseI2CWriteBytes(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element is not hexadecimal string"); } // Test where fails: masks value is invalid try { const json element = R"( { "register": "0x0A", "values": [ "0xCC", "0xFF" ], "masks": [ "F", "0x7F" ] } )"_json; parseI2CWriteBytes(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element is not hexadecimal string"); } // Test where fails: number of elements in masks is invalid try { const json element = R"( { "register": "0x0A", "values": [ "0xCC", "0xFF" ], "masks": [ "0x7F" ] } )"_json; parseI2CWriteBytes(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Invalid number of elements in masks"); } // Test where fails: Required register property not specified try { const json element = R"( { "values": [ "0xCC", "0xFF" ] } )"_json; parseI2CWriteBytes(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Required property missing: register"); } // Test where fails: Required values property not specified try { const json element = R"( { "register": "0x0A" } )"_json; parseI2CWriteBytes(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Required property missing: values"); } } TEST(ConfigFileParserTests, ParseIf) { // Test where works: Only required properties specified { const json element = R"( { "condition": { "run_rule": "is_downlevel_regulator" }, "then": [ { "run_rule": "configure_downlevel_regulator" }, { "run_rule": "configure_standard_regulator" } ] } )"_json; std::unique_ptr action = parseIf(element); EXPECT_NE(action->getConditionAction().get(), nullptr); EXPECT_EQ(action->getThenActions().size(), 2); EXPECT_EQ(action->getElseActions().size(), 0); } // Test where works: All properties specified { const json element = R"( { "condition": { "run_rule": "is_downlevel_regulator" }, "then": [ { "run_rule": "configure_downlevel_regulator" } ], "else": [ { "run_rule": "configure_standard_regulator" } ] } )"_json; std::unique_ptr action = parseIf(element); EXPECT_NE(action->getConditionAction().get(), nullptr); EXPECT_EQ(action->getThenActions().size(), 1); EXPECT_EQ(action->getElseActions().size(), 1); } // Test where fails: Required condition property not specified try { const json element = R"( { "then": [ { "run_rule": "configure_downlevel_regulator" } ], "else": [ { "run_rule": "configure_standard_regulator" } ] } )"_json; parseIf(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Required property missing: condition"); } // Test where fails: Required then property not specified try { const json element = R"( { "condition": { "run_rule": "is_downlevel_regulator" }, "else": [ { "run_rule": "configure_standard_regulator" } ] } )"_json; parseIf(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Required property missing: then"); } // Test where fails: condition value is invalid try { const json element = R"( { "condition": 1, "then": [ { "run_rule": "configure_downlevel_regulator" } ], "else": [ { "run_rule": "configure_standard_regulator" } ] } )"_json; parseIf(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element is not an object"); } // Test where fails: then value is invalid try { const json element = R"( { "condition": { "run_rule": "is_downlevel_regulator" }, "then": "foo", "else": [ { "run_rule": "configure_standard_regulator" } ] } )"_json; parseIf(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element is not an array"); } // Test where fails: else value is invalid try { const json element = R"( { "condition": { "run_rule": "is_downlevel_regulator" }, "then": [ { "run_rule": "configure_downlevel_regulator" } ], "else": 1 } )"_json; parseIf(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element is not an array"); } // Test where fails: Invalid property specified try { const json element = R"( { "condition": { "run_rule": "is_downlevel_regulator" }, "then": [ { "run_rule": "configure_downlevel_regulator" } ], "foo": "bar" } )"_json; parseIf(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element contains an invalid property"); } // Test where fails: Element is not an object try { const json element = R"( [ "0xFF", "0x01" ] )"_json; parseIf(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element is not an object"); } } TEST(ConfigFileParserTests, ParseInt8) { // Test where works: INT8_MIN { const json element = R"( -128 )"_json; int8_t value = parseInt8(element); EXPECT_EQ(value, -128); } // Test where works: INT8_MAX { const json element = R"( 127 )"_json; int8_t value = parseInt8(element); EXPECT_EQ(value, 127); } // Test where fails: Element is not an integer try { const json element = R"( 1.03 )"_json; parseInt8(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element is not an integer"); } // Test where fails: Value < INT8_MIN try { const json element = R"( -129 )"_json; parseInt8(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element is not an 8-bit signed integer"); } // Test where fails: Value > INT8_MAX try { const json element = R"( 128 )"_json; parseInt8(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element is not an 8-bit signed integer"); } } TEST(ConfigFileParserTests, ParseInventoryPath) { // Test where works: Inventory path has a leading '/' { const json element = "/system/chassis/motherboard/cpu3"; std::string value = parseInventoryPath(element); EXPECT_EQ( value, "/xyz/openbmc_project/inventory/system/chassis/motherboard/cpu3"); } // Test where works: Inventory path does not have a leading '/' { const json element = "system/chassis/motherboard/cpu1"; std::string value = parseInventoryPath(element); EXPECT_EQ( value, "/xyz/openbmc_project/inventory/system/chassis/motherboard/cpu1"); } // Test where fails: JSON element is not a string try { const json element = R"( { "foo": "bar" } )"_json; parseInventoryPath(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element is not a string"); } // Test where fails: JSON element contains an empty string try { const json element = ""; parseInventoryPath(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element contains an empty string"); } } TEST(ConfigFileParserTests, ParseLogPhaseFault) { // Test where works { const json element = R"( { "type": "n+1" } )"_json; std::unique_ptr action = parseLogPhaseFault(element); EXPECT_EQ(action->getType(), PhaseFaultType::n_plus_1); } // Test where fails: Element is not an object try { const json element = R"( [ "0xFF", "0x01" ] )"_json; parseLogPhaseFault(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element is not an object"); } // Test where fails: Required type property not specified try { const json element = R"( { } )"_json; parseLogPhaseFault(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Required property missing: type"); } // Test where fails: type value is invalid try { const json element = R"( { "type": "n+2" } )"_json; parseLogPhaseFault(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element is not a phase fault type"); } // Test where fails: Invalid property specified try { const json element = R"( { "type": "n+1", "foo": 1 } )"_json; parseLogPhaseFault(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element contains an invalid property"); } } TEST(ConfigFileParserTests, ParseNot) { // Test where works { const json element = R"( { "i2c_compare_byte": { "register": "0xA0", "value": "0x00" } } )"_json; std::unique_ptr action = parseNot(element); EXPECT_NE(action->getAction().get(), nullptr); } // Test where fails: Element is not an object try { const json element = R"( [ "0xFF", "0x01" ] )"_json; parseNot(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element is not an object"); } } TEST(ConfigFileParserTests, ParseOr) { // Test where works: Element is an array with 2 actions { const json element = R"( [ { "i2c_compare_byte": { "register": "0xA0", "value": "0x00" } }, { "i2c_compare_byte": { "register": "0xA1", "value": "0x00" } } ] )"_json; std::unique_ptr action = parseOr(element); EXPECT_EQ(action->getActions().size(), 2); } // Test where fails: Element is an array with 1 action try { const json element = R"( [ { "i2c_compare_byte": { "register": "0xA0", "value": "0x00" } } ] )"_json; parseOr(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Array must contain two or more actions"); } // Test where fails: Element is not an array try { const json element = R"( { "foo": "bar" } )"_json; parseOr(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element is not an array"); } } TEST(ConfigFileParserTests, ParsePhaseFaultDetection) { // Test where works: actions specified: optional properties not specified { const json element = R"( { "actions": [ { "run_rule": "detect_phase_fault_rule" } ] } )"_json; std::unique_ptr phaseFaultDetection = parsePhaseFaultDetection(element); EXPECT_EQ(phaseFaultDetection->getActions().size(), 1); EXPECT_EQ(phaseFaultDetection->getDeviceID(), ""); } // Test where works: rule_id specified: optional properties specified { const json element = R"( { "comments": [ "Detect phase fault using I/O expander" ], "device_id": "io_expander", "rule_id": "detect_phase_fault_rule" } )"_json; std::unique_ptr phaseFaultDetection = parsePhaseFaultDetection(element); EXPECT_EQ(phaseFaultDetection->getActions().size(), 1); EXPECT_EQ(phaseFaultDetection->getDeviceID(), "io_expander"); } // Test where fails: Element is not an object try { const json element = R"( [ "foo", "bar" ] )"_json; parsePhaseFaultDetection(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element is not an object"); } // Test where fails: device_id value is invalid try { const json element = R"( { "device_id": 1, "rule_id": "detect_phase_fault_rule" } )"_json; parsePhaseFaultDetection(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element is not a string"); } // Test where fails: rule_id value is invalid try { const json element = R"( { "rule_id": 1 } )"_json; parsePhaseFaultDetection(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element is not a string"); } // Test where fails: actions object is invalid try { const json element = R"( { "actions": 1 } )"_json; parsePhaseFaultDetection(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element is not an array"); } // Test where fails: Required actions or rule_id property not specified try { const json element = R"( { "device_id": "io_expander" } )"_json; parsePhaseFaultDetection(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Invalid property combination: Must contain " "either rule_id or actions"); } // Test where fails: Required actions or rule_id property both specified try { const json element = R"( { "rule_id": "detect_phase_fault_rule", "actions": [ { "run_rule": "detect_phase_fault_rule" } ] } )"_json; parsePhaseFaultDetection(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Invalid property combination: Must contain " "either rule_id or actions"); } // Test where fails: Invalid property specified try { const json element = R"( { "foo": "bar", "actions": [ { "run_rule": "detect_phase_fault_rule" } ] } )"_json; parsePhaseFaultDetection(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element contains an invalid property"); } } TEST(ConfigFileParserTests, ParsePhaseFaultType) { // Test where works: n { const json element = "n"; PhaseFaultType type = parsePhaseFaultType(element); EXPECT_EQ(type, PhaseFaultType::n); } // Test where works: n+1 { const json element = "n+1"; PhaseFaultType type = parsePhaseFaultType(element); EXPECT_EQ(type, PhaseFaultType::n_plus_1); } // Test where fails: Element is not a phase fault type try { const json element = "n+2"; parsePhaseFaultType(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element is not a phase fault type"); } // Test where fails: Element is not a string try { const json element = R"( { "foo": "bar" } )"_json; parsePhaseFaultType(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element is not a string"); } } TEST(ConfigFileParserTests, ParsePMBusReadSensor) { // Test where works: Only required properties specified { const json element = R"( { "type": "iout", "command": "0x8C", "format": "linear_11" } )"_json; std::unique_ptr action = parsePMBusReadSensor(element); EXPECT_EQ(action->getType(), SensorType::iout); EXPECT_EQ(action->getCommand(), 0x8C); EXPECT_EQ(action->getFormat(), pmbus_utils::SensorDataFormat::linear_11); EXPECT_EQ(action->getExponent().has_value(), false); } // Test where works: All properties specified { const json element = R"( { "type": "temperature", "command": "0x7A", "format": "linear_16", "exponent": -8 } )"_json; std::unique_ptr action = parsePMBusReadSensor(element); EXPECT_EQ(action->getType(), SensorType::temperature); EXPECT_EQ(action->getCommand(), 0x7A); EXPECT_EQ(action->getFormat(), pmbus_utils::SensorDataFormat::linear_16); EXPECT_EQ(action->getExponent().has_value(), true); EXPECT_EQ(action->getExponent().value(), -8); } // Test where fails: Element is not an object try { const json element = R"( [ "0xFF", "0x01" ] )"_json; parsePMBusReadSensor(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element is not an object"); } // Test where fails: Invalid property specified try { const json element = R"( { "type": "iout", "command": "0x8C", "format": "linear_11", "foo": 1 } )"_json; parsePMBusReadSensor(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element contains an invalid property"); } // Test where fails: Required type property not specified try { const json element = R"( { "command": "0x8C", "format": "linear_11" } )"_json; parsePMBusReadSensor(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Required property missing: type"); } // Test where fails: Required command property not specified try { const json element = R"( { "type": "iout", "format": "linear_11" } )"_json; parsePMBusReadSensor(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Required property missing: command"); } // Test where fails: Required format property not specified try { const json element = R"( { "type": "iout", "command": "0x8C" } )"_json; parsePMBusReadSensor(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Required property missing: format"); } // Test where fails: type value is invalid try { const json element = R"( { "type": 1, "command": "0x7A", "format": "linear_16" } )"_json; parsePMBusReadSensor(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element is not a string"); } // Test where fails: command value is invalid try { const json element = R"( { "type": "temperature", "command": 0, "format": "linear_16" } )"_json; parsePMBusReadSensor(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element is not a string"); } // Test where fails: format value is invalid try { const json element = R"( { "type": "temperature", "command": "0x7A", "format": 1 } )"_json; parsePMBusReadSensor(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element is not a string"); } // Test where fails: exponent value is invalid try { const json element = R"( { "type": "temperature", "command": "0x7A", "format": "linear_16", "exponent": 1.3 } )"_json; parsePMBusReadSensor(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element is not an integer"); } } TEST(ConfigFileParserTests, ParsePMBusWriteVoutCommand) { // Test where works: Only required properties specified { const json element = R"( { "format": "linear" } )"_json; std::unique_ptr action = parsePMBusWriteVoutCommand(element); EXPECT_EQ(action->getVolts().has_value(), false); EXPECT_EQ(action->getFormat(), pmbus_utils::VoutDataFormat::linear); EXPECT_EQ(action->getExponent().has_value(), false); EXPECT_EQ(action->isVerified(), false); } // Test where works: All properties specified { const json element = R"( { "volts": 1.03, "format": "linear", "exponent": -8, "is_verified": true } )"_json; std::unique_ptr action = parsePMBusWriteVoutCommand(element); EXPECT_EQ(action->getVolts().has_value(), true); EXPECT_EQ(action->getVolts().value(), 1.03); EXPECT_EQ(action->getFormat(), pmbus_utils::VoutDataFormat::linear); EXPECT_EQ(action->getExponent().has_value(), true); EXPECT_EQ(action->getExponent().value(), -8); EXPECT_EQ(action->isVerified(), true); } // Test where fails: Element is not an object try { const json element = R"( [ "0xFF", "0x01" ] )"_json; parsePMBusWriteVoutCommand(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element is not an object"); } // Test where fails: volts value is invalid try { const json element = R"( { "volts": "foo", "format": "linear" } )"_json; parsePMBusWriteVoutCommand(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element is not a number"); } // Test where fails: Required format property not specified try { const json element = R"( { "volts": 1.03, "is_verified": true } )"_json; parsePMBusWriteVoutCommand(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Required property missing: format"); } // Test where fails: format value is invalid try { const json element = R"( { "format": "linear_11" } )"_json; parsePMBusWriteVoutCommand(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Invalid format value: linear_11"); } // Test where fails: exponent value is invalid try { const json element = R"( { "format": "linear", "exponent": 1.3 } )"_json; parsePMBusWriteVoutCommand(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element is not an integer"); } // Test where fails: is_verified value is invalid try { const json element = R"( { "format": "linear", "is_verified": "true" } )"_json; parsePMBusWriteVoutCommand(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element is not a boolean"); } // Test where fails: Invalid property specified try { const json element = R"( { "format": "linear", "foo": "bar" } )"_json; parsePMBusWriteVoutCommand(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element contains an invalid property"); } } TEST(ConfigFileParserTests, ParsePresenceDetection) { // Test where works: actions property specified { const json element = R"( { "actions": [ { "run_rule": "read_sensors_rule" } ] } )"_json; std::unique_ptr presenceDetection = parsePresenceDetection(element); EXPECT_EQ(presenceDetection->getActions().size(), 1); } // Test where works: rule_id property specified { const json element = R"( { "comments": [ "comments property" ], "rule_id": "set_voltage_rule" } )"_json; std::unique_ptr presenceDetection = parsePresenceDetection(element); EXPECT_EQ(presenceDetection->getActions().size(), 1); } // Test where fails: actions object is invalid try { const json element = R"( { "actions": 1 } )"_json; parsePresenceDetection(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element is not an array"); } // Test where fails: rule_id value is invalid try { const json element = R"( { "rule_id": 1 } )"_json; parsePresenceDetection(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element is not a string"); } // Test where fails: Required actions or rule_id property not specified try { const json element = R"( { "comments": [ "comments property" ] } )"_json; parsePresenceDetection(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Invalid property combination: Must contain " "either rule_id or actions"); } // Test where fails: Required actions or rule_id property both specified try { const json element = R"( { "rule_id": "set_voltage_rule", "actions": [ { "run_rule": "read_sensors_rule" } ] } )"_json; parsePresenceDetection(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Invalid property combination: Must contain " "either rule_id or actions"); } // Test where fails: Element is not an object try { const json element = R"( [ "foo", "bar" ] )"_json; parsePresenceDetection(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element is not an object"); } // Test where fails: Invalid property specified try { const json element = R"( { "foo": "bar", "actions": [ { "run_rule": "read_sensors_rule" } ] } )"_json; parsePresenceDetection(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element contains an invalid property"); } } TEST(ConfigFileParserTests, ParseRail) { // Test where works: Only required properties specified { const json element = R"( { "id": "vdd" } )"_json; std::unique_ptr rail = parseRail(element); EXPECT_EQ(rail->getID(), "vdd"); EXPECT_EQ(rail->getConfiguration(), nullptr); EXPECT_EQ(rail->getSensorMonitoring(), nullptr); } // Test where works: All properties specified { const json element = R"( { "comments": [ "comments property" ], "id": "vdd", "configuration": { "volts": 1.1, "actions": [ { "pmbus_write_vout_command": { "format": "linear" } } ] }, "sensor_monitoring": { "actions": [ { "run_rule": "read_sensors_rule" } ] } } )"_json; std::unique_ptr rail = parseRail(element); EXPECT_EQ(rail->getID(), "vdd"); EXPECT_NE(rail->getConfiguration(), nullptr); EXPECT_NE(rail->getSensorMonitoring(), nullptr); } // Test where fails: id property not specified try { const json element = R"( { "configuration": { "volts": 1.1, "actions": [ { "pmbus_write_vout_command": { "format": "linear" } } ] } } )"_json; parseRail(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Required property missing: id"); } // Test where fails: id property is invalid try { const json element = R"( { "id": "", "configuration": { "volts": 1.1, "actions": [ { "pmbus_write_vout_command": { "format": "linear" } } ] } } )"_json; parseRail(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element contains an empty string"); } // Test where fails: Element is not an object try { const json element = R"( [ "0xFF", "0x01" ] )"_json; parseRail(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element is not an object"); } // Test where fails: configuration value is invalid try { const json element = R"( { "id": "vdd", "configuration": "config" } )"_json; parseRail(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element is not an object"); } // Test where fails: sensor_monitoring value is invalid try { const json element = R"( { "comments": [ "comments property" ], "id": "vdd", "configuration": { "volts": 1.1, "actions": [ { "pmbus_write_vout_command": { "format": "linear" } } ] }, "sensor_monitoring": 1 } )"_json; parseRail(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element is not an object"); } // Test where fails: Invalid property specified try { const json element = R"( { "id": "vdd", "foo" : true } )"_json; parseRail(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element contains an invalid property"); } } TEST(ConfigFileParserTests, ParseRailArray) { // Test where works { const json element = R"( [ { "id": "vdd" }, { "id": "vio" } ] )"_json; std::vector> rails = parseRailArray(element); EXPECT_EQ(rails.size(), 2); EXPECT_EQ(rails[0]->getID(), "vdd"); EXPECT_EQ(rails[1]->getID(), "vio"); } // Test where fails: Element is not an array try { const json element = R"( { "foo": "bar" } )"_json; parseRailArray(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element is not an array"); } } TEST(ConfigFileParserTests, ParseRoot) { // Test where works: Only required properties specified { const json element = R"( { "chassis": [ { "number": 1, "inventory_path": "system/chassis" } ] } )"_json; std::vector> rules{}; std::vector> chassis{}; std::tie(rules, chassis) = parseRoot(element); EXPECT_EQ(rules.size(), 0); EXPECT_EQ(chassis.size(), 1); } // Test where works: All properties specified { const json element = R"( { "comments": [ "Config file for a FooBar one-chassis system" ], "rules": [ { "id": "set_voltage_rule", "actions": [ { "pmbus_write_vout_command": { "format": "linear" } } ] } ], "chassis": [ { "number": 1, "inventory_path": "system/chassis1" }, { "number": 3, "inventory_path": "system/chassis3" } ] } )"_json; std::vector> rules{}; std::vector> chassis{}; std::tie(rules, chassis) = parseRoot(element); EXPECT_EQ(rules.size(), 1); EXPECT_EQ(chassis.size(), 2); } // Test where fails: Element is not an object try { const json element = R"( [ "0xFF", "0x01" ] )"_json; parseRoot(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element is not an object"); } // Test where fails: chassis property not specified try { const json element = R"( { "rules": [ { "id": "set_voltage_rule", "actions": [ { "pmbus_write_vout_command": { "format": "linear" } } ] } ] } )"_json; parseRoot(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Required property missing: chassis"); } // Test where fails: Invalid property specified try { const json element = R"( { "remarks": [ "Config file for a FooBar one-chassis system" ], "chassis": [ { "number": 1, "inventory_path": "system/chassis" } ] } )"_json; parseRoot(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element contains an invalid property"); } } TEST(ConfigFileParserTests, ParseRule) { // Test where works: comments property specified { const json element = R"( { "comments": [ "Set voltage rule" ], "id": "set_voltage_rule", "actions": [ { "pmbus_write_vout_command": { "volts": 1.01, "format": "linear" } }, { "pmbus_write_vout_command": { "volts": 1.03, "format": "linear" } } ] } )"_json; std::unique_ptr rule = parseRule(element); EXPECT_EQ(rule->getID(), "set_voltage_rule"); EXPECT_EQ(rule->getActions().size(), 2); } // Test where works: comments property not specified { const json element = R"( { "id": "set_voltage_rule", "actions": [ { "pmbus_write_vout_command": { "volts": 1.01, "format": "linear" } }, { "pmbus_write_vout_command": { "volts": 1.03, "format": "linear" } }, { "pmbus_write_vout_command": { "volts": 1.05, "format": "linear" } } ] } )"_json; std::unique_ptr rule = parseRule(element); EXPECT_EQ(rule->getID(), "set_voltage_rule"); EXPECT_EQ(rule->getActions().size(), 3); } // Test where fails: Element is not an object try { const json element = R"( [ "0xFF", "0x01" ] )"_json; parseRule(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element is not an object"); } // Test where fails: id property not specified try { const json element = R"( { "actions": [ { "pmbus_write_vout_command": { "volts": 1.01, "format": "linear" } } ] } )"_json; parseRule(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Required property missing: id"); } // Test where fails: id property is invalid try { const json element = R"( { "id": "", "actions": [ { "pmbus_write_vout_command": { "volts": 1.01, "format": "linear" } } ] } )"_json; parseRule(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element contains an empty string"); } // Test where fails: actions property not specified try { const json element = R"( { "comments": [ "Set voltage rule" ], "id": "set_voltage_rule" } )"_json; parseRule(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Required property missing: actions"); } // Test where fails: actions property is invalid try { const json element = R"( { "id": "set_voltage_rule", "actions": true } )"_json; parseRule(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element is not an array"); } // Test where fails: Invalid property specified try { const json element = R"( { "remarks": [ "Set voltage rule" ], "id": "set_voltage_rule", "actions": [ { "pmbus_write_vout_command": { "volts": 1.01, "format": "linear" } } ] } )"_json; parseRule(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element contains an invalid property"); } } TEST(ConfigFileParserTests, ParseRuleArray) { // Test where works { const json element = R"( [ { "id": "set_voltage_rule1", "actions": [ { "pmbus_write_vout_command": { "volts": 1.01, "format": "linear" } } ] }, { "id": "set_voltage_rule2", "actions": [ { "pmbus_write_vout_command": { "volts": 1.01, "format": "linear" } }, { "pmbus_write_vout_command": { "volts": 1.11, "format": "linear" } } ] } ] )"_json; std::vector> rules = parseRuleArray(element); EXPECT_EQ(rules.size(), 2); EXPECT_EQ(rules[0]->getID(), "set_voltage_rule1"); EXPECT_EQ(rules[0]->getActions().size(), 1); EXPECT_EQ(rules[1]->getID(), "set_voltage_rule2"); EXPECT_EQ(rules[1]->getActions().size(), 2); } // Test where fails: Element is not an array try { const json element = R"( { "id": "set_voltage_rule" } )"_json; parseRuleArray(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element is not an array"); } } TEST(ConfigFileParserTests, ParseRuleIDOrActionsProperty) { // Test where works: actions specified { const json element = R"( { "actions": [ { "pmbus_write_vout_command": { "format": "linear" } }, { "run_rule": "set_voltage_rule" } ] } )"_json; std::vector> actions = parseRuleIDOrActionsProperty(element); EXPECT_EQ(actions.size(), 2); } // Test where works: rule_id specified { const json element = R"( { "rule_id": "set_voltage_rule" } )"_json; std::vector> actions = parseRuleIDOrActionsProperty(element); EXPECT_EQ(actions.size(), 1); } // Test where fails: Element is not an object try { const json element = R"( [ "foo", "bar" ] )"_json; parseRuleIDOrActionsProperty(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element is not an object"); } // Test where fails: rule_id is invalid try { const json element = R"( { "rule_id": 1 } )"_json; parseRuleIDOrActionsProperty(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element is not a string"); } // Test where fails: actions is invalid try { const json element = R"( { "actions": 1 } )"_json; parseRuleIDOrActionsProperty(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element is not an array"); } // Test where fails: Neither rule_id nor actions specified try { const json element = R"( { "volts": 1.03 } )"_json; parseRuleIDOrActionsProperty(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Invalid property combination: Must contain " "either rule_id or actions"); } // Test where fails: Both rule_id and actions specified try { const json element = R"( { "volts": 1.03, "rule_id": "set_voltage_rule", "actions": [ { "pmbus_write_vout_command": { "format": "linear" } } ] } )"_json; parseRuleIDOrActionsProperty(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Invalid property combination: Must contain " "either rule_id or actions"); } } TEST(ConfigFileParserTests, ParseRunRule) { // Test where works { const json element = "vdd_regulator"; std::unique_ptr action = parseRunRule(element); EXPECT_EQ(action->getRuleID(), "vdd_regulator"); } // Test where fails: Element is not a string try { const json element = 1; parseRunRule(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element is not a string"); } // Test where fails: Empty string try { const json element = ""; parseRunRule(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element contains an empty string"); } } TEST(ConfigFileParserTests, ParseSensorDataFormat) { // Test where works: linear_11 { const json element = "linear_11"; pmbus_utils::SensorDataFormat value = parseSensorDataFormat(element); pmbus_utils::SensorDataFormat format = pmbus_utils::SensorDataFormat::linear_11; EXPECT_EQ(value, format); } // Test where works: linear_16 { const json element = "linear_16"; pmbus_utils::SensorDataFormat value = parseSensorDataFormat(element); pmbus_utils::SensorDataFormat format = pmbus_utils::SensorDataFormat::linear_16; EXPECT_EQ(value, format); } // Test where fails: Element is not a sensor data format try { const json element = "foo"; parseSensorDataFormat(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element is not a sensor data format"); } // Test where fails: Element is not a string try { const json element = R"( { "foo": "bar" } )"_json; parseSensorDataFormat(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element is not a string"); } } TEST(ConfigFileParserTests, ParseSensorMonitoring) { // Test where works: actions property specified { const json element = R"( { "actions": [ { "run_rule": "read_sensors_rule" } ] } )"_json; std::unique_ptr sensorMonitoring = parseSensorMonitoring(element); EXPECT_EQ(sensorMonitoring->getActions().size(), 1); } // Test where works: rule_id property specified { const json element = R"( { "comments": [ "comments property" ], "rule_id": "set_voltage_rule" } )"_json; std::unique_ptr sensorMonitoring = parseSensorMonitoring(element); EXPECT_EQ(sensorMonitoring->getActions().size(), 1); } // Test where fails: actions object is invalid try { const json element = R"( { "actions": 1 } )"_json; parseSensorMonitoring(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element is not an array"); } // Test where fails: rule_id value is invalid try { const json element = R"( { "rule_id": 1 } )"_json; parseSensorMonitoring(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element is not a string"); } // Test where fails: Required actions or rule_id property not specified try { const json element = R"( { "comments": [ "comments property" ] } )"_json; parseSensorMonitoring(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Invalid property combination: Must contain " "either rule_id or actions"); } // Test where fails: Required actions or rule_id property both specified try { const json element = R"( { "rule_id": "set_voltage_rule", "actions": [ { "run_rule": "read_sensors_rule" } ] } )"_json; parseSensorMonitoring(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Invalid property combination: Must contain " "either rule_id or actions"); } // Test where fails: Element is not an object try { const json element = R"( [ "foo", "bar" ] )"_json; parseSensorMonitoring(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element is not an object"); } // Test where fails: Invalid property specified try { const json element = R"( { "foo": "bar", "actions": [ { "run_rule": "read_sensors_rule" } ] } )"_json; parseSensorMonitoring(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element contains an invalid property"); } } TEST(ConfigFileParserTests, ParseSensorType) { // Test where works: iout { const json element = "iout"; SensorType type = parseSensorType(element); EXPECT_EQ(type, SensorType::iout); } // Test where works: iout_peak { const json element = "iout_peak"; SensorType type = parseSensorType(element); EXPECT_EQ(type, SensorType::iout_peak); } // Test where works: iout_valley { const json element = "iout_valley"; SensorType type = parseSensorType(element); EXPECT_EQ(type, SensorType::iout_valley); } // Test where works: pout { const json element = "pout"; SensorType type = parseSensorType(element); EXPECT_EQ(type, SensorType::pout); } // Test where works: temperature { const json element = "temperature"; SensorType type = parseSensorType(element); EXPECT_EQ(type, SensorType::temperature); } // Test where works: temperature_peak { const json element = "temperature_peak"; SensorType type = parseSensorType(element); EXPECT_EQ(type, SensorType::temperature_peak); } // Test where works: vout { const json element = "vout"; SensorType type = parseSensorType(element); EXPECT_EQ(type, SensorType::vout); } // Test where works: vout_peak { const json element = "vout_peak"; SensorType type = parseSensorType(element); EXPECT_EQ(type, SensorType::vout_peak); } // Test where works: vout_valley { const json element = "vout_valley"; SensorType type = parseSensorType(element); EXPECT_EQ(type, SensorType::vout_valley); } // Test where fails: Element is not a sensor type try { const json element = "foo"; parseSensorType(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element is not a sensor type"); } // Test where fails: Element is not a string try { const json element = R"( { "foo": "bar" } )"_json; parseSensorType(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element is not a string"); } } TEST(ConfigFileParserTests, ParseSetDevice) { // Test where works { const json element = "regulator1"; std::unique_ptr action = parseSetDevice(element); EXPECT_EQ(action->getDeviceID(), "regulator1"); } // Test where fails: Element is not a string try { const json element = 1; parseSetDevice(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element is not a string"); } // Test where fails: Empty string try { const json element = ""; parseSetDevice(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element contains an empty string"); } } TEST(ConfigFileParserTests, ParseString) { // Test where works: Empty string { const json element = ""; std::string value = parseString(element, true); EXPECT_EQ(value, ""); } // Test where works: Non-empty string { const json element = "vdd_regulator"; std::string value = parseString(element, false); EXPECT_EQ(value, "vdd_regulator"); } // Test where fails: Element is not a string try { const json element = R"( { "foo": "bar" } )"_json; parseString(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element is not a string"); } // Test where fails: Empty string try { const json element = ""; parseString(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element contains an empty string"); } } TEST(ConfigFileParserTests, ParseUint8) { // Test where works: 0 { const json element = R"( 0 )"_json; uint8_t value = parseUint8(element); EXPECT_EQ(value, 0); } // Test where works: UINT8_MAX { const json element = R"( 255 )"_json; uint8_t value = parseUint8(element); EXPECT_EQ(value, 255); } // Test where fails: Element is not an integer try { const json element = R"( 1.03 )"_json; parseUint8(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element is not an integer"); } // Test where fails: Value < 0 try { const json element = R"( -1 )"_json; parseUint8(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element is not an 8-bit unsigned integer"); } // Test where fails: Value > UINT8_MAX try { const json element = R"( 256 )"_json; parseUint8(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element is not an 8-bit unsigned integer"); } } TEST(ConfigFileParserTests, ParseUnsignedInteger) { // Test where works: 1 { const json element = R"( 1 )"_json; unsigned int value = parseUnsignedInteger(element); EXPECT_EQ(value, 1); } // Test where fails: Element is not an integer try { const json element = R"( 1.5 )"_json; parseUnsignedInteger(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element is not an unsigned integer"); } // Test where fails: Value < 0 try { const json element = R"( -1 )"_json; parseUnsignedInteger(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element is not an unsigned integer"); } } TEST(ConfigFileParserTests, ParseVoutDataFormat) { // Test where works: linear { const json element = "linear"; pmbus_utils::VoutDataFormat value = parseVoutDataFormat(element); pmbus_utils::VoutDataFormat format = pmbus_utils::VoutDataFormat::linear; EXPECT_EQ(value, format); } // Test where works: vid { const json element = "vid"; pmbus_utils::VoutDataFormat value = parseVoutDataFormat(element); pmbus_utils::VoutDataFormat format = pmbus_utils::VoutDataFormat::vid; EXPECT_EQ(value, format); } // Test where works: direct { const json element = "direct"; pmbus_utils::VoutDataFormat value = parseVoutDataFormat(element); pmbus_utils::VoutDataFormat format = pmbus_utils::VoutDataFormat::direct; EXPECT_EQ(value, format); } // Test where works: ieee { const json element = "ieee"; pmbus_utils::VoutDataFormat value = parseVoutDataFormat(element); pmbus_utils::VoutDataFormat format = pmbus_utils::VoutDataFormat::ieee; EXPECT_EQ(value, format); } // Test where fails: Element is not a vout data format try { const json element = "foo"; parseVoutDataFormat(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element is not a vout data format"); } // Test where fails: Element is not a string try { const json element = R"( { "foo": "bar" } )"_json; parseVoutDataFormat(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element is not a string"); } } TEST(ConfigFileParserTests, VerifyIsArray) { // Test where element is an array try { const json element = R"( [ "foo", "bar" ] )"_json; verifyIsArray(element); } catch (const std::exception& e) { ADD_FAILURE() << "Should not have caught exception."; } // Test where element is not an array try { const json element = R"( { "foo": "bar" } )"_json; verifyIsArray(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element is not an array"); } } TEST(ConfigFileParserTests, VerifyIsObject) { // Test where element is an object try { const json element = R"( { "foo": "bar" } )"_json; verifyIsObject(element); } catch (const std::exception& e) { ADD_FAILURE() << "Should not have caught exception."; } // Test where element is not an object try { const json element = R"( [ "foo", "bar" ] )"_json; verifyIsObject(element); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element is not an object"); } } TEST(ConfigFileParserTests, VerifyPropertyCount) { // Test where element has expected number of properties try { const json element = R"( { "comments": [ "Set voltage rule" ], "id": "set_voltage_rule" } )"_json; verifyPropertyCount(element, 2); } catch (const std::exception& e) { ADD_FAILURE() << "Should not have caught exception."; } // Test where element has unexpected number of properties try { const json element = R"( { "comments": [ "Set voltage rule" ], "id": "set_voltage_rule", "foo": 1.3 } )"_json; verifyPropertyCount(element, 2); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Element contains an invalid property"); } }