Create common json_parser_utils functions
Create a json_parser_utils namespace containing common functions for
parsing JSON.
Extract the common functions from the JSON parsing code in the
phosphor-regulators and phosphor-power-sequencer applications.
Both applications have some identical parsing functions.
Create a common ConfigFileParserError class. The phosphor-regulators and
phosphor-power-sequencer applications both have an identical version of
this exception class.
Extract the common test cases from the two applications and put them in
a common location as well.
Summary:
* Common JSON parsing functions in
phosphor-power-sequencer/src/config_file_parser.* and
phosphor-regulators/src/config_file_parser.* moved to
json_parser_utils.*
* Common test cases in
phosphor-power-sequencer/test/config_file_parser_tests.cpp and
phosphor-regulators/test/config_file_parser_tests.cpp moved to
test/json_parser_utils_tests.cpp
* phosphor-power-sequencer/src/config_file_parser_error.hpp and
phosphor-regulators/src/config_file_parser_error.hpp replaced with
config_file_parser_error.hpp
* phosphor-power-sequencer/test/config_file_parser_error_tests.cpp and
phosphor-regulators/test/config_file_parser_error_tests.cpp replaced
with test/config_file_parser_error_tests.cpp
Tested:
* Ran automated test cases.
Change-Id: I35074c5e42d9e89def41ba8e729fe11c54ed8d27
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 e8d68d5..07566ae 100644
--- a/phosphor-power-sequencer/src/config_file_parser.cpp
+++ b/phosphor-power-sequencer/src/config_file_parser.cpp
@@ -17,11 +17,16 @@
#include "config_file_parser.hpp"
#include "config_file_parser_error.hpp"
+#include "json_parser_utils.hpp"
+#include <cstdint>
#include <exception>
#include <fstream>
#include <optional>
+#include <stdexcept>
+using namespace phosphor::power::json_parser_utils;
+using ConfigFileParserError = phosphor::power::util::ConfigFileParserError;
using json = nlohmann::json;
namespace fs = std::filesystem;
diff --git a/phosphor-power-sequencer/src/config_file_parser.hpp b/phosphor-power-sequencer/src/config_file_parser.hpp
index 7b28f2f..ee12690 100644
--- a/phosphor-power-sequencer/src/config_file_parser.hpp
+++ b/phosphor-power-sequencer/src/config_file_parser.hpp
@@ -19,10 +19,8 @@
#include <nlohmann/json.hpp>
-#include <cstdint>
#include <filesystem>
#include <memory>
-#include <stdexcept>
#include <string>
#include <vector>
@@ -78,50 +76,6 @@
{
/**
- * Returns the specified property of the specified JSON element.
- *
- * Throws an invalid_argument exception if the property does not exist.
- *
- * @param element JSON element
- * @param property property name
- */
-#pragma GCC diagnostic push
-#if __GNUC__ >= 13
-#pragma GCC diagnostic ignored "-Wdangling-reference"
-#endif
-inline const nlohmann::json& getRequiredProperty(const nlohmann::json& element,
- const std::string& property)
-{
- auto it = element.find(property);
- if (it == element.end())
- {
- throw std::invalid_argument{"Required property missing: " + property};
- }
- return *it;
-}
-#pragma GCC diagnostic pop
-
-/**
- * Parses a JSON element containing a boolean.
- *
- * Returns the corresponding C++ boolean value.
- *
- * Throws an exception if parsing fails.
- *
- * @param element JSON element
- * @return boolean value
- */
-inline bool parseBoolean(const nlohmann::json& element)
-{
- // Verify element contains a boolean
- if (!element.is_boolean())
- {
- throw std::invalid_argument{"Element is not a boolean"};
- }
- return element.get<bool>();
-}
-
-/**
* Parses a JSON element containing a GPIO.
*
* Returns the corresponding C++ GPIO object.
@@ -170,127 +124,6 @@
*/
std::vector<std::unique_ptr<Rail>> parseRoot(const nlohmann::json& element);
-/**
- * Parses a JSON element containing a string.
- *
- * Returns the corresponding C++ string.
- *
- * Throws an exception if parsing fails.
- *
- * @param element JSON element
- * @param isEmptyValid indicates whether an empty string value is valid
- * @return string value
- */
-inline std::string parseString(const nlohmann::json& element,
- bool isEmptyValid = false)
-{
- if (!element.is_string())
- {
- throw std::invalid_argument{"Element is not a string"};
- }
- std::string value = element.get<std::string>();
- if (value.empty() && !isEmptyValid)
- {
- throw std::invalid_argument{"Element contains an empty string"};
- }
- return value;
-}
-
-/**
- * Parses a JSON element containing an 8-bit unsigned integer.
- *
- * Returns the corresponding C++ uint8_t value.
- *
- * Throws an exception if parsing fails.
- *
- * @param element JSON element
- * @return uint8_t value
- */
-inline uint8_t parseUint8(const nlohmann::json& element)
-{
- // Verify element contains an integer
- if (!element.is_number_integer())
- {
- throw std::invalid_argument{"Element is not an integer"};
- }
- int value = element.get<int>();
- if ((value < 0) || (value > UINT8_MAX))
- {
- throw std::invalid_argument{"Element is not an 8-bit unsigned integer"};
- }
- return static_cast<uint8_t>(value);
-}
-
-/**
- * Parses a JSON element containing an unsigned integer.
- *
- * Returns the corresponding C++ unsigned int value.
- *
- * Throws an exception if parsing fails.
- *
- * @param element JSON element
- * @return unsigned int value
- */
-inline unsigned int parseUnsignedInteger(const nlohmann::json& element)
-{
- // Verify element contains an unsigned integer
- if (!element.is_number_unsigned())
- {
- throw std::invalid_argument{"Element is not an unsigned integer"};
- }
- return element.get<unsigned int>();
-}
-
-/**
- * Verifies that the specified JSON element is a JSON array.
- *
- * Throws an invalid_argument exception if the element is not an array.
- *
- * @param element JSON element
- */
-inline void verifyIsArray(const nlohmann::json& element)
-{
- if (!element.is_array())
- {
- throw std::invalid_argument{"Element is not an array"};
- }
-}
-
-/**
- * Verifies that the specified JSON element is a JSON object.
- *
- * Throws an invalid_argument exception if the element is not an object.
- *
- * @param element JSON element
- */
-inline void verifyIsObject(const nlohmann::json& element)
-{
- if (!element.is_object())
- {
- throw std::invalid_argument{"Element is not an object"};
- }
-}
-
-/**
- * Verifies that the specified JSON element contains the expected number of
- * properties.
- *
- * Throws an invalid_argument exception if the element contains a different
- * number of properties. This indicates the element contains an invalid
- * property.
- *
- * @param element JSON element
- * @param expectedCount expected number of properties in element
- */
-inline void verifyPropertyCount(const nlohmann::json& element,
- unsigned int expectedCount)
-{
- if (element.size() != expectedCount)
- {
- throw std::invalid_argument{"Element contains an invalid property"};
- }
-}
-
} // namespace internal
} // namespace phosphor::power::sequencer::config_file_parser
diff --git a/phosphor-power-sequencer/src/config_file_parser_error.hpp b/phosphor-power-sequencer/src/config_file_parser_error.hpp
deleted file mode 100644
index 4c77952..0000000
--- a/phosphor-power-sequencer/src/config_file_parser_error.hpp
+++ /dev/null
@@ -1,85 +0,0 @@
-/**
- * Copyright © 2024 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.
- */
-#pragma once
-
-#include <exception>
-#include <filesystem>
-#include <string>
-
-namespace phosphor::power::sequencer
-{
-
-/**
- * @class ConfigFileParserError
- *
- * An error that occurred while parsing a JSON configuration file.
- */
-class ConfigFileParserError : public std::exception
-{
- public:
- // Specify which compiler-generated methods we want
- ConfigFileParserError() = delete;
- ConfigFileParserError(const ConfigFileParserError&) = default;
- ConfigFileParserError(ConfigFileParserError&&) = default;
- ConfigFileParserError& operator=(const ConfigFileParserError&) = default;
- ConfigFileParserError& operator=(ConfigFileParserError&&) = default;
- virtual ~ConfigFileParserError() = default;
-
- /**
- * Constructor.
- *
- * @param pathName Configuration file path name
- * @param error Error message
- */
- explicit ConfigFileParserError(const std::filesystem::path& pathName,
- const std::string& error) :
- pathName{pathName},
- error{"ConfigFileParserError: " + pathName.string() + ": " + error}
- {}
-
- /**
- * Returns the configuration file path name.
- *
- * @return path name
- */
- const std::filesystem::path& getPathName()
- {
- return pathName;
- }
-
- /**
- * Returns the description of this error.
- *
- * @return error description
- */
- const char* what() const noexcept override
- {
- return error.c_str();
- }
-
- private:
- /**
- * Configuration file path name.
- */
- const std::filesystem::path pathName;
-
- /**
- * Error message.
- */
- const std::string error{};
-};
-
-} // namespace phosphor::power::sequencer
diff --git a/phosphor-power-sequencer/test/config_file_parser_error_tests.cpp b/phosphor-power-sequencer/test/config_file_parser_error_tests.cpp
deleted file mode 100644
index 9af389a..0000000
--- a/phosphor-power-sequencer/test/config_file_parser_error_tests.cpp
+++ /dev/null
@@ -1,53 +0,0 @@
-/**
- * Copyright © 2024 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 "config_file_parser_error.hpp"
-
-#include <filesystem>
-
-#include <gtest/gtest.h>
-
-using namespace phosphor::power::sequencer;
-
-TEST(ConfigFileParserErrorTests, Constructor)
-{
- std::filesystem::path pathName{
- "/usr/share/phosphor-power-sequencer/foo.json"};
- ConfigFileParserError error{pathName, "Required property missing"};
- EXPECT_EQ(error.getPathName(), pathName);
- EXPECT_STREQ(
- error.what(),
- "ConfigFileParserError: /usr/share/phosphor-power-sequencer/foo.json: "
- "Required property missing");
-}
-
-TEST(ConfigFileParserErrorTests, GetPathName)
-{
- std::filesystem::path pathName{
- "/usr/share/phosphor-power-sequencer/bar.json"};
- ConfigFileParserError error{pathName, "Required property missing"};
- EXPECT_EQ(error.getPathName(), pathName);
-}
-
-TEST(ConfigFileParserErrorTests, What)
-{
- std::filesystem::path pathName{
- "/usr/share/phosphor-power-sequencer/bar.json"};
- ConfigFileParserError error{pathName, "Required property missing"};
- EXPECT_STREQ(
- error.what(),
- "ConfigFileParserError: /usr/share/phosphor-power-sequencer/bar.json: "
- "Required property missing");
-}
diff --git a/phosphor-power-sequencer/test/config_file_parser_tests.cpp b/phosphor-power-sequencer/test/config_file_parser_tests.cpp
index 6f8a878..9ad21c8 100644
--- a/phosphor-power-sequencer/test/config_file_parser_tests.cpp
+++ b/phosphor-power-sequencer/test/config_file_parser_tests.cpp
@@ -23,7 +23,6 @@
#include <nlohmann/json.hpp>
-#include <cstdint>
#include <exception>
#include <filesystem>
#include <fstream>
@@ -322,57 +321,6 @@
}
}
-TEST(ConfigFileParserTests, GetRequiredProperty)
-{
- // Test where property exists
- {
- const json element = R"( { "name": "VDD_CPU0" } )"_json;
- const json& propertyElement = getRequiredProperty(element, "name");
- EXPECT_EQ(propertyElement.get<std::string>(), "VDD_CPU0");
- }
-
- // Test where property does not exist
- try
- {
- const json element = R"( { "foo": 23 } )"_json;
- getRequiredProperty(element, "name");
- ADD_FAILURE() << "Should not have reached this line.";
- }
- catch (const std::invalid_argument& e)
- {
- EXPECT_STREQ(e.what(), "Required property missing: name");
- }
-}
-
-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, ParseGPIO)
{
// Test where works: Only required properties specified
@@ -879,205 +827,3 @@
EXPECT_STREQ(e.what(), "Element contains an invalid property");
}
}
-
-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_cpu1";
- std::string value = parseString(element, false);
- EXPECT_EQ(value, "vdd_cpu1");
- }
-
- // 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, VerifyIsArray)
-{
- // Test where element is an array
- {
- const json element = R"( [ "foo", "bar" ] )"_json;
- verifyIsArray(element);
- }
-
- // 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
- {
- const json element = R"( { "foo": "bar" } )"_json;
- verifyIsObject(element);
- }
-
- // 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
- {
- const json element = R"(
- {
- "line": 131,
- "active_low": true
- }
- )"_json;
- verifyPropertyCount(element, 2);
- }
-
- // Test where element has unexpected number of properties
- try
- {
- const json element = R"(
- {
- "line": 131,
- "active_low": true,
- "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");
- }
-}
diff --git a/phosphor-power-sequencer/test/meson.build b/phosphor-power-sequencer/test/meson.build
index 7bd92b7..2827c3f 100644
--- a/phosphor-power-sequencer/test/meson.build
+++ b/phosphor-power-sequencer/test/meson.build
@@ -3,7 +3,6 @@
executable(
'phosphor-power-sequencer-tests',
'chassis_tests.cpp',
- 'config_file_parser_error_tests.cpp',
'config_file_parser_tests.cpp',
'pmbus_driver_device_tests.cpp',
'rail_tests.cpp',