pseq: Parsing support for chassis element
Add JSON parsing support for the new chassis element in the
phosphor-power-sequencer configuration file.
Tested:
* Ran automated tests.
Change-Id: I563e3db7515108b4fb301e9208f6da22a4934227
Signed-off-by: Shawn McCarney <shawnmm@us.ibm.com>
diff --git a/phosphor-power-sequencer/src/config_file_parser.cpp b/phosphor-power-sequencer/src/config_file_parser.cpp
index 6d9b169..18fe32e 100644
--- a/phosphor-power-sequencer/src/config_file_parser.cpp
+++ b/phosphor-power-sequencer/src/config_file_parser.cpp
@@ -94,6 +94,125 @@
namespace internal
{
+std::unique_ptr<Chassis> parseChassis(
+ const json& element, std::map<std::string, JSONRefWrapper> chassisTemplates,
+ Services& services)
+{
+ verifyIsObject(element);
+
+ // If chassis object is not using a template, parse properties normally
+ if (!element.contains("template_id"))
+ {
+ bool isChassisTemplate{false};
+ return parseChassisProperties(element, isChassisTemplate, NO_VARIABLES,
+ services);
+ }
+
+ // Parse chassis object that is using a template
+ unsigned int propertyCount{0};
+
+ // Optional comments property; value not stored
+ if (element.contains("comments"))
+ {
+ ++propertyCount;
+ }
+
+ // Required template_id property
+ const json& templateIDElement = getRequiredProperty(element, "template_id");
+ std::string templateID = parseString(templateIDElement);
+ ++propertyCount;
+
+ // Required template_variable_values property
+ const json& variablesElement =
+ getRequiredProperty(element, "template_variable_values");
+ std::map<std::string, std::string> variables =
+ parseVariables(variablesElement);
+ ++propertyCount;
+
+ // Verify no invalid properties exist
+ verifyPropertyCount(element, propertyCount);
+
+ // Get reference to chassis template JSON
+ auto it = chassisTemplates.find(templateID);
+ if (it == chassisTemplates.end())
+ {
+ throw std::invalid_argument{
+ "Invalid chassis template id: " + templateID};
+ }
+ const json& templateElement = it->second.get();
+
+ // Parse properties in template using variable values for this chassis
+ bool isChassisTemplate{true};
+ return parseChassisProperties(templateElement, isChassisTemplate, variables,
+ services);
+}
+
+std::vector<std::unique_ptr<Chassis>> parseChassisArray(
+ const json& element, std::map<std::string, JSONRefWrapper> chassisTemplates,
+ Services& services)
+{
+ verifyIsArray(element);
+ std::vector<std::unique_ptr<Chassis>> chassis;
+ for (auto& chassisElement : element)
+ {
+ chassis.emplace_back(
+ parseChassis(chassisElement, chassisTemplates, services));
+ }
+ return chassis;
+}
+
+std::unique_ptr<Chassis> parseChassisProperties(
+ const json& element, bool isChassisTemplate,
+ const std::map<std::string, std::string>& variables, Services& services)
+
+{
+ verifyIsObject(element);
+ unsigned int propertyCount{0};
+
+ // Optional comments property; value not stored
+ if (element.contains("comments"))
+ {
+ ++propertyCount;
+ }
+
+ // Required id property if this is a chassis template
+ // Don't parse again; this was already parsed by parseChassisTemplate()
+ if (isChassisTemplate)
+ {
+ getRequiredProperty(element, "id");
+ ++propertyCount;
+ }
+
+ // Required number property
+ const json& numberElement = getRequiredProperty(element, "number");
+ unsigned int number = parseUnsignedInteger(numberElement, variables);
+ if (number < 1)
+ {
+ throw std::invalid_argument{"Invalid chassis number: Must be > 0"};
+ }
+ ++propertyCount;
+
+ // Required inventory_path property
+ const json& inventoryPathElement =
+ getRequiredProperty(element, "inventory_path");
+ std::string inventoryPath =
+ parseString(inventoryPathElement, false, variables);
+ ++propertyCount;
+
+ // Required power_sequencers property
+ const json& powerSequencersElement =
+ getRequiredProperty(element, "power_sequencers");
+ std::vector<std::unique_ptr<PowerSequencerDevice>> powerSequencers =
+ parsePowerSequencerArray(powerSequencersElement, variables, services);
+ ++propertyCount;
+
+ // Verify no invalid properties exist
+ verifyPropertyCount(element, propertyCount);
+
+ return std::make_unique<Chassis>(number, inventoryPath,
+ std::move(powerSequencers));
+}
+
std::tuple<std::string, JSONRefWrapper> parseChassisTemplate(
const json& element)
{
@@ -368,6 +487,21 @@
return rails;
}
+std::map<std::string, std::string> parseVariables(const json& element)
+{
+ verifyIsObject(element);
+
+ std::map<std::string, std::string> variables;
+ std::string name, value;
+ for (const auto& [nameElement, valueElement] : element.items())
+ {
+ name = parseString(nameElement);
+ value = parseString(valueElement);
+ variables.emplace(name, value);
+ }
+ return variables;
+}
+
} // namespace internal
} // namespace phosphor::power::sequencer::config_file_parser
diff --git a/phosphor-power-sequencer/src/config_file_parser.hpp b/phosphor-power-sequencer/src/config_file_parser.hpp
index b2a18ca..665029e 100644
--- a/phosphor-power-sequencer/src/config_file_parser.hpp
+++ b/phosphor-power-sequencer/src/config_file_parser.hpp
@@ -15,6 +15,7 @@
*/
#pragma once
+#include "chassis.hpp"
#include "power_sequencer_device.hpp"
#include "rail.hpp"
#include "services.hpp"
@@ -86,6 +87,57 @@
using JSONRefWrapper = std::reference_wrapper<const json>;
/**
+ * Parses a JSON element containing a chassis object.
+ *
+ * Returns the corresponding C++ Chassis object.
+ *
+ * Throws an exception if parsing fails.
+ *
+ * @param element JSON element
+ * @param chassisTemplates chassis templates map
+ * @param services system services like hardware presence and the journal
+ * @return Chassis object
+ */
+std::unique_ptr<Chassis> parseChassis(
+ const json& element, std::map<std::string, JSONRefWrapper> chassisTemplates,
+ Services& services);
+
+/**
+ * Parses a JSON element containing an array of chassis objects.
+ *
+ * Returns the corresponding C++ Chassis objects.
+ *
+ * Throws an exception if parsing fails.
+ *
+ * @param element JSON element
+ * @param chassisTemplates chassis templates map
+ * @param services system services like hardware presence and the journal
+ * @return vector of Chassis objects
+ */
+std::vector<std::unique_ptr<Chassis>> parseChassisArray(
+ const json& element, std::map<std::string, JSONRefWrapper> chassisTemplates,
+ Services& services);
+
+/**
+ * Parses a JSON element containing the properties of a chassis.
+ *
+ * The JSON element may be a chassis object or chassis_template object.
+ *
+ * Returns the corresponding C++ Chassis object.
+ *
+ * Throws an exception if parsing fails.
+ *
+ * @param element JSON element
+ * @param isChassisTemplate specifies whether element is a chassis_template
+ * @param variables variables map used to expand variables in element value
+ * @param services system services like hardware presence and the journal
+ * @return Chassis object
+ */
+std::unique_ptr<Chassis> parseChassisProperties(
+ const json& element, bool isChassisTemplate,
+ const std::map<std::string, std::string>& variables, Services& services);
+
+/**
* Parses a JSON element containing a chassis_template object.
*
* Returns the template ID and a C++ reference_wrapper to the JSON element.
@@ -198,6 +250,18 @@
*/
std::vector<std::unique_ptr<Rail>> parseRoot(const json& element);
+/**
+ * Parses a JSON element containing an object with variable names and values.
+ *
+ * Returns the corresponding C++ map of variable names and values.
+ *
+ * Throws an exception if parsing fails.
+ *
+ * @param element JSON element
+ * @return map of variable names and values
+ */
+std::map<std::string, std::string> parseVariables(const json& element);
+
} // namespace internal
} // namespace phosphor::power::sequencer::config_file_parser
diff --git a/phosphor-power-sequencer/test/config_file_parser_tests.cpp b/phosphor-power-sequencer/test/config_file_parser_tests.cpp
index a4a8040..3f0797c 100644
--- a/phosphor-power-sequencer/test/config_file_parser_tests.cpp
+++ b/phosphor-power-sequencer/test/config_file_parser_tests.cpp
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+#include "chassis.hpp"
#include "config_file_parser.hpp"
#include "config_file_parser_error.hpp"
#include "mock_services.hpp"
@@ -326,6 +327,705 @@
}
}
+TEST(ConfigFileParserTests, ParseChassis)
+{
+ // Constants used by multiple tests
+ const json templateElement = R"(
+ {
+ "id": "foo_chassis",
+ "number": "${chassis_number}",
+ "inventory_path": "/xyz/openbmc_project/inventory/system/chassis${chassis_number}",
+ "power_sequencers": [
+ {
+ "type": "UCD90320",
+ "i2c_interface": { "bus": "${bus}", "address": "${address}" },
+ "power_control_gpio_name": "power-chassis${chassis_number}-control",
+ "power_good_gpio_name": "power-chassis${chassis_number}-good",
+ "rails": []
+ }
+ ]
+ }
+ )"_json;
+ const std::map<std::string, JSONRefWrapper> chassisTemplates{
+ {"foo_chassis", templateElement}};
+
+ // Test where works: Does not use a template
+ {
+ const json element = R"(
+ {
+ "number": 1,
+ "inventory_path": "/xyz/openbmc_project/inventory/system/chassis",
+ "power_sequencers": [
+ {
+ "type": "UCD90320",
+ "i2c_interface": { "bus": 3, "address": "0x11" },
+ "power_control_gpio_name": "power-chassis-control",
+ "power_good_gpio_name": "power-chassis-good",
+ "rails": []
+ }
+ ]
+ }
+ )"_json;
+ MockServices services{};
+ auto chassis = parseChassis(element, chassisTemplates, services);
+ EXPECT_EQ(chassis->getNumber(), 1);
+ EXPECT_EQ(chassis->getInventoryPath(),
+ "/xyz/openbmc_project/inventory/system/chassis");
+ EXPECT_EQ(chassis->getPowerSequencers().size(), 1);
+ EXPECT_EQ(chassis->getPowerSequencers()[0]->getName(), "UCD90320");
+ EXPECT_EQ(chassis->getPowerSequencers()[0]->getBus(), 3);
+ EXPECT_EQ(chassis->getPowerSequencers()[0]->getAddress(), 0x11);
+ }
+
+ // Test where works: Uses template: No comments specified
+ {
+ const json element = R"(
+ {
+ "template_id": "foo_chassis",
+ "template_variable_values": {
+ "chassis_number": "2",
+ "bus": "13",
+ "address": "0x70"
+ }
+ }
+ )"_json;
+ MockServices services{};
+ auto chassis = parseChassis(element, chassisTemplates, services);
+ EXPECT_EQ(chassis->getNumber(), 2);
+ EXPECT_EQ(chassis->getInventoryPath(),
+ "/xyz/openbmc_project/inventory/system/chassis2");
+ EXPECT_EQ(chassis->getPowerSequencers().size(), 1);
+ EXPECT_EQ(chassis->getPowerSequencers()[0]->getName(), "UCD90320");
+ EXPECT_EQ(chassis->getPowerSequencers()[0]->getBus(), 13);
+ EXPECT_EQ(chassis->getPowerSequencers()[0]->getAddress(), 0x70);
+ }
+
+ // Test where works: Uses template: Comments specified
+ {
+ const json element = R"(
+ {
+ "comments": ["Chassis 3: Standard hardware layout"],
+ "template_id": "foo_chassis",
+ "template_variable_values": {
+ "chassis_number": "3",
+ "bus": "23",
+ "address": "0x54"
+ }
+ }
+ )"_json;
+ MockServices services{};
+ auto chassis = parseChassis(element, chassisTemplates, services);
+ EXPECT_EQ(chassis->getNumber(), 3);
+ EXPECT_EQ(chassis->getInventoryPath(),
+ "/xyz/openbmc_project/inventory/system/chassis3");
+ EXPECT_EQ(chassis->getPowerSequencers().size(), 1);
+ EXPECT_EQ(chassis->getPowerSequencers()[0]->getName(), "UCD90320");
+ EXPECT_EQ(chassis->getPowerSequencers()[0]->getBus(), 23);
+ EXPECT_EQ(chassis->getPowerSequencers()[0]->getAddress(), 0x54);
+ }
+
+ // Test where fails: Element is not an object
+ try
+ {
+ const json element = R"( [ "vdda", "vddb" ] )"_json;
+ MockServices services{};
+ parseChassis(element, chassisTemplates, services);
+ 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: Does not use a template: Cannot parse properties
+ try
+ {
+ const json element = R"(
+ {
+ "number": "one",
+ "inventory_path": "/xyz/openbmc_project/inventory/system/chassis",
+ "power_sequencers": []
+ }
+ )"_json;
+ MockServices services{};
+ parseChassis(element, chassisTemplates, services);
+ 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: Uses template: Required template_variable_values
+ // property not specified
+ try
+ {
+ const json element = R"(
+ {
+ "template_id": "foo_chassis"
+ }
+ )"_json;
+ MockServices services{};
+ parseChassis(element, chassisTemplates, services);
+ ADD_FAILURE() << "Should not have reached this line.";
+ }
+ catch (const std::invalid_argument& e)
+ {
+ EXPECT_STREQ(e.what(),
+ "Required property missing: template_variable_values");
+ }
+
+ // Test where fails: Uses template: template_id value is invalid: Not a
+ // string
+ try
+ {
+ const json element = R"(
+ {
+ "template_id": 23,
+ "template_variable_values": { "chassis_number": "2" }
+ }
+ )"_json;
+ MockServices services{};
+ parseChassis(element, chassisTemplates, services);
+ 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: Uses template: template_id value is invalid: No
+ // matching template
+ try
+ {
+ const json element = R"(
+ {
+ "template_id": "does_not_exist",
+ "template_variable_values": { "chassis_number": "2" }
+ }
+ )"_json;
+ MockServices services{};
+ parseChassis(element, chassisTemplates, services);
+ ADD_FAILURE() << "Should not have reached this line.";
+ }
+ catch (const std::invalid_argument& e)
+ {
+ EXPECT_STREQ(e.what(), "Invalid chassis template id: does_not_exist");
+ }
+
+ // Test where fails: Uses template: template_variable_values value is
+ // invalid
+ try
+ {
+ const json element = R"(
+ {
+ "template_id": "foo_chassis",
+ "template_variable_values": { "chassis_number": 2 }
+ }
+ )"_json;
+ MockServices services{};
+ parseChassis(element, chassisTemplates, services);
+ 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: Uses template: Invalid property specified
+ try
+ {
+ const json element = R"(
+ {
+ "template_id": "foo_chassis",
+ "template_variable_values": { "chassis_number": "2" },
+ "foo": "bar"
+ }
+ )"_json;
+ MockServices services{};
+ parseChassis(element, chassisTemplates, services);
+ 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: Uses template: Cannot parse properties in template
+ try
+ {
+ const json element = R"(
+ {
+ "template_id": "foo_chassis",
+ "template_variable_values": { "chassis_number": "0" }
+ }
+ )"_json;
+ MockServices services{};
+ parseChassis(element, chassisTemplates, services);
+ 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(ConfigFileParserTests, ParseChassisArray)
+{
+ // Constants used by multiple tests
+ const json fooTemplateElement = R"(
+ {
+ "id": "foo_chassis",
+ "number": "${chassis_number}",
+ "inventory_path": "/xyz/openbmc_project/inventory/system/chassis${chassis_number}",
+ "power_sequencers": []
+ }
+ )"_json;
+ const json barTemplateElement = R"(
+ {
+ "id": "bar_chassis",
+ "number": "${chassis_number}",
+ "inventory_path": "/xyz/openbmc_project/inventory/system/bar_chassis${chassis_number}",
+ "power_sequencers": []
+ }
+ )"_json;
+ const std::map<std::string, JSONRefWrapper> chassisTemplates{
+ {"foo_chassis", fooTemplateElement},
+ {"bar_chassis", barTemplateElement}};
+
+ // Test where works: Array is empty
+ {
+ const json element = R"(
+ [
+ ]
+ )"_json;
+ MockServices services{};
+ auto chassis = parseChassisArray(element, chassisTemplates, services);
+ EXPECT_EQ(chassis.size(), 0);
+ }
+
+ // Test where works: Template not used
+ {
+ const json element = R"(
+ [
+ {
+ "number": 1,
+ "inventory_path": "/xyz/openbmc_project/inventory/system/chassis1",
+ "power_sequencers": []
+ },
+ {
+ "number": 2,
+ "inventory_path": "/xyz/openbmc_project/inventory/system/chassis2",
+ "power_sequencers": []
+ }
+ ]
+ )"_json;
+ MockServices services{};
+ auto chassis = parseChassisArray(element, chassisTemplates, services);
+ 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 works: Template used
+ {
+ const json element = R"(
+ [
+ {
+ "template_id": "foo_chassis",
+ "template_variable_values": { "chassis_number": "2" }
+ },
+ {
+ "template_id": "bar_chassis",
+ "template_variable_values": { "chassis_number": "3" }
+ }
+ ]
+ )"_json;
+ MockServices services{};
+ auto chassis = parseChassisArray(element, chassisTemplates, services);
+ EXPECT_EQ(chassis.size(), 2);
+ EXPECT_EQ(chassis[0]->getNumber(), 2);
+ EXPECT_EQ(chassis[0]->getInventoryPath(),
+ "/xyz/openbmc_project/inventory/system/chassis2");
+ EXPECT_EQ(chassis[1]->getNumber(), 3);
+ EXPECT_EQ(chassis[1]->getInventoryPath(),
+ "/xyz/openbmc_project/inventory/system/bar_chassis3");
+ }
+
+ // Test where fails: Element is not an array
+ try
+ {
+ const json element = R"(
+ {
+ "foo": "bar"
+ }
+ )"_json;
+ MockServices services{};
+ parseChassisArray(element, chassisTemplates, services);
+ 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: Element within array is invalid
+ try
+ {
+ const json element = R"(
+ [
+ {
+ "number": true,
+ "inventory_path": "/xyz/openbmc_project/inventory/system/chassis",
+ "power_sequencers": []
+ }
+ ]
+ )"_json;
+ MockServices services{};
+ parseChassisArray(element, chassisTemplates, services);
+ 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: Invalid variable value specified
+ try
+ {
+ const json element = R"(
+ [
+ {
+ "template_id": "foo_chassis",
+ "template_variable_values": { "chassis_number": "two" }
+ }
+ ]
+ )"_json;
+ MockServices services{};
+ parseChassisArray(element, chassisTemplates, services);
+ 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, ParseChassisProperties)
+{
+ // Test where works: Parse chassis object without template/variables: Has
+ // comments property
+ {
+ const json element = R"(
+ {
+ "comments": [ "Chassis 1: Has all CPUs, fans, and PSUs" ],
+ "number": 1,
+ "inventory_path": "/xyz/openbmc_project/inventory/system/chassis",
+ "power_sequencers": [
+ {
+ "type": "UCD90160",
+ "i2c_interface": { "bus": 3, "address": "0x11" },
+ "power_control_gpio_name": "power-chassis-control",
+ "power_good_gpio_name": "power-chassis-good",
+ "rails": [ { "name": "VDD_CPU0" }, { "name": "VCS_CPU1" } ]
+ }
+ ]
+ }
+ )"_json;
+ bool isChassisTemplate{false};
+ std::map<std::string, std::string> variables{};
+ MockServices services{};
+ auto chassis = parseChassisProperties(element, isChassisTemplate,
+ variables, services);
+ EXPECT_EQ(chassis->getNumber(), 1);
+ EXPECT_EQ(chassis->getInventoryPath(),
+ "/xyz/openbmc_project/inventory/system/chassis");
+ EXPECT_EQ(chassis->getPowerSequencers().size(), 1);
+ EXPECT_EQ(chassis->getPowerSequencers()[0]->getName(), "UCD90160");
+ EXPECT_EQ(chassis->getPowerSequencers()[0]->getBus(), 3);
+ EXPECT_EQ(chassis->getPowerSequencers()[0]->getAddress(), 0x11);
+ EXPECT_EQ(chassis->getPowerSequencers()[0]->getPowerControlGPIOName(),
+ "power-chassis-control");
+ EXPECT_EQ(chassis->getPowerSequencers()[0]->getPowerGoodGPIOName(),
+ "power-chassis-good");
+ EXPECT_EQ(chassis->getPowerSequencers()[0]->getRails().size(), 2);
+ EXPECT_EQ(chassis->getPowerSequencers()[0]->getRails()[0]->getName(),
+ "VDD_CPU0");
+ EXPECT_EQ(chassis->getPowerSequencers()[0]->getRails()[1]->getName(),
+ "VCS_CPU1");
+ }
+
+ // Test where works: Parse chassis_template object with variables: No
+ // comments property
+ {
+ const json element = R"(
+ {
+ "id": "foo_chassis",
+ "number": "${chassis_number}",
+ "inventory_path": "/xyz/openbmc_project/inventory/system/chassis${chassis_number}",
+ "power_sequencers": [
+ {
+ "type": "UCD90320",
+ "i2c_interface": { "bus": "${bus}", "address": "${address}" },
+ "power_control_gpio_name": "power-chassis${chassis_number}-control",
+ "power_good_gpio_name": "power-chassis${chassis_number}-good",
+ "rails": [ { "name": "vio${chassis_number}" } ]
+ }
+ ]
+ }
+ )"_json;
+ bool isChassisTemplate{true};
+ std::map<std::string, std::string> variables{
+ {"chassis_number", "2"}, {"bus", "12"}, {"address", "0x71"}};
+ MockServices services{};
+ auto chassis = parseChassisProperties(element, isChassisTemplate,
+ variables, services);
+ EXPECT_EQ(chassis->getNumber(), 2);
+ EXPECT_EQ(chassis->getInventoryPath(),
+ "/xyz/openbmc_project/inventory/system/chassis2");
+ EXPECT_EQ(chassis->getPowerSequencers().size(), 1);
+ EXPECT_EQ(chassis->getPowerSequencers()[0]->getName(), "UCD90320");
+ EXPECT_EQ(chassis->getPowerSequencers()[0]->getBus(), 12);
+ EXPECT_EQ(chassis->getPowerSequencers()[0]->getAddress(), 0x71);
+ EXPECT_EQ(chassis->getPowerSequencers()[0]->getPowerControlGPIOName(),
+ "power-chassis2-control");
+ EXPECT_EQ(chassis->getPowerSequencers()[0]->getPowerGoodGPIOName(),
+ "power-chassis2-good");
+ EXPECT_EQ(chassis->getPowerSequencers()[0]->getRails().size(), 1);
+ EXPECT_EQ(chassis->getPowerSequencers()[0]->getRails()[0]->getName(),
+ "vio2");
+ }
+
+ // Test where fails: Element is not an object
+ try
+ {
+ const json element = R"( true )"_json;
+ bool isChassisTemplate{false};
+ std::map<std::string, std::string> variables{};
+ MockServices services{};
+ parseChassisProperties(element, isChassisTemplate, variables, services);
+ 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 in chassis template
+ try
+ {
+ const json element = R"(
+ {
+ "number": "${chassis_number}",
+ "inventory_path": "/xyz/openbmc_project/inventory/system/chassis${chassis_number}",
+ "power_sequencers": []
+ }
+ )"_json;
+ bool isChassisTemplate{true};
+ std::map<std::string, std::string> variables{{"chassis_number", "2"}};
+ MockServices services{};
+ parseChassisProperties(element, isChassisTemplate, variables, services);
+ 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 number property not specified
+ try
+ {
+ const json element = R"(
+ {
+ "inventory_path": "/xyz/openbmc_project/inventory/system/chassis",
+ "power_sequencers": []
+ }
+ )"_json;
+ bool isChassisTemplate{false};
+ std::map<std::string, std::string> variables{};
+ MockServices services{};
+ parseChassisProperties(element, isChassisTemplate, variables, services);
+ 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"(
+ {
+ "id": "foo_chassis",
+ "number": "${chassis_number}",
+ "power_sequencers": []
+ }
+ )"_json;
+ bool isChassisTemplate{true};
+ std::map<std::string, std::string> variables{{"chassis_number", "2"}};
+ MockServices services{};
+ parseChassisProperties(element, isChassisTemplate, variables, services);
+ 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: Required power_sequencers property not specified
+ try
+ {
+ const json element = R"(
+ {
+ "number": 1,
+ "inventory_path": "/xyz/openbmc_project/inventory/system/chassis"
+ }
+ )"_json;
+ bool isChassisTemplate{false};
+ std::map<std::string, std::string> variables{};
+ MockServices services{};
+ parseChassisProperties(element, isChassisTemplate, variables, services);
+ ADD_FAILURE() << "Should not have reached this line.";
+ }
+ catch (const std::invalid_argument& e)
+ {
+ EXPECT_STREQ(e.what(), "Required property missing: power_sequencers");
+ }
+
+ // Test where fails: number value is invalid: Not an integer
+ try
+ {
+ const json element = R"(
+ {
+ "number": "two",
+ "inventory_path": "/xyz/openbmc_project/inventory/system/chassis",
+ "power_sequencers": []
+ }
+ )"_json;
+ bool isChassisTemplate{false};
+ std::map<std::string, std::string> variables{};
+ MockServices services{};
+ parseChassisProperties(element, isChassisTemplate, variables, services);
+ 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: number value is invalid: Equal to 0
+ try
+ {
+ const json element = R"(
+ {
+ "number": 0,
+ "inventory_path": "/xyz/openbmc_project/inventory/system/chassis",
+ "power_sequencers": []
+ }
+ )"_json;
+ bool isChassisTemplate{false};
+ std::map<std::string, std::string> variables{};
+ MockServices services{};
+ parseChassisProperties(element, isChassisTemplate, variables, services);
+ 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: inventory_path value is invalid
+ try
+ {
+ const json element = R"(
+ {
+ "number": 1,
+ "inventory_path": "",
+ "power_sequencers": []
+ }
+ )"_json;
+ bool isChassisTemplate{false};
+ std::map<std::string, std::string> variables{};
+ MockServices services{};
+ parseChassisProperties(element, isChassisTemplate, variables, services);
+ 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: power_sequencers value is invalid
+ try
+ {
+ const json element = R"(
+ {
+ "number": 1,
+ "inventory_path": "/xyz/openbmc_project/inventory/system/chassis",
+ "power_sequencers": { "name": "foo" }
+ }
+ )"_json;
+ bool isChassisTemplate{false};
+ std::map<std::string, std::string> variables{};
+ MockServices services{};
+ parseChassisProperties(element, isChassisTemplate, variables, services);
+ 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"(
+ {
+ "foo": "bar",
+ "number": 1,
+ "inventory_path": "/xyz/openbmc_project/inventory/system/chassis",
+ "power_sequencers": []
+ }
+ )"_json;
+ bool isChassisTemplate{false};
+ std::map<std::string, std::string> variables{};
+ MockServices services{};
+ parseChassisProperties(element, isChassisTemplate, variables, services);
+ 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 variable value specified
+ try
+ {
+ const json element = R"(
+ {
+ "id": "foo_chassis",
+ "number": "${chassis_number}",
+ "inventory_path": "/xyz/openbmc_project/inventory/system/chassis${chassis_number}",
+ "power_sequencers": []
+ }
+ )"_json;
+ bool isChassisTemplate{true};
+ std::map<std::string, std::string> variables{{"chassis_number", "two"}};
+ MockServices services{};
+ parseChassisProperties(element, isChassisTemplate, variables, services);
+ 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, ParseChassisTemplate)
{
// Test where works: comments specified
@@ -1822,3 +2522,113 @@
EXPECT_STREQ(e.what(), "Element contains an invalid property");
}
}
+
+TEST(ConfigFileParserTests, ParseVariables)
+{
+ // Test where works: No variables specified
+ {
+ const json element = R"(
+ {
+ }
+ )"_json;
+ auto variables = parseVariables(element);
+ EXPECT_EQ(variables.size(), 0);
+ }
+
+ // Test where works: Variables specified
+ {
+ const json element = R"(
+ {
+ "chassis_number": "2",
+ "bus_number": "13"
+ }
+ )"_json;
+ auto variables = parseVariables(element);
+ EXPECT_EQ(variables.size(), 2);
+ EXPECT_EQ(variables["chassis_number"], "2");
+ EXPECT_EQ(variables["bus_number"], "13");
+ }
+
+ // Test where fails: Element is not an object
+ try
+ {
+ const json element = R"(
+ [
+ "chassis_number", "2",
+ "bus_number", "13"
+ ]
+ )"_json;
+ parseVariables(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: Key is not a string
+ try
+ {
+ const json element = R"(
+ {
+ chassis_number: "2",
+ "bus_number": "13"
+ }
+ )"_json;
+ parseVariables(element);
+ ADD_FAILURE() << "Should not have reached this line.";
+ }
+ catch (const json::parse_error& e)
+ {}
+
+ // Test where fails: Value is not a string
+ try
+ {
+ const json element = R"(
+ {
+ "chassis_number": "2",
+ "bus_number": 13
+ }
+ )"_json;
+ parseVariables(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: Key is an empty string
+ try
+ {
+ const json element = R"(
+ {
+ "chassis_number": "2",
+ "": "13"
+ }
+ )"_json;
+ parseVariables(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: Value is an empty string
+ try
+ {
+ const json element = R"(
+ {
+ "chassis_number": "",
+ "bus_number": "13"
+ }
+ )"_json;
+ parseVariables(element);
+ ADD_FAILURE() << "Should not have reached this line.";
+ }
+ catch (const std::invalid_argument& e)
+ {
+ EXPECT_STREQ(e.what(), "Element contains an empty string");
+ }
+}