Create validate-regulators-config_tests.cpp.
Create validate-regulators-config_tests.cpp and add test cases for JSON
object "rule". The validate-regulators-config_tests.cpp tests the cases
which validate-regulators-config.py checks.
Signed-off-by: Bob King <Bob_King@wistron.com>
Change-Id: I86ad7d78dac3617170dbd0a78213843f311b7123
diff --git a/phosphor-regulators/test/validate-regulators-config_tests.cpp b/phosphor-regulators/test/validate-regulators-config_tests.cpp
new file mode 100644
index 0000000..08e798d
--- /dev/null
+++ b/phosphor-regulators/test/validate-regulators-config_tests.cpp
@@ -0,0 +1,289 @@
+/**
+ * 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 <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/wait.h>
+
+#include <fstream>
+#include <nlohmann/json.hpp>
+
+#include <gtest/gtest.h>
+
+#define EXPECT_FILE_VALID(configFile) expectFileValid(configFile)
+#define EXPECT_FILE_INVALID(configFile, expectedErrorMessage, \
+ expectedOutputMessage) \
+ expectFileInvalid(configFile, expectedErrorMessage, expectedOutputMessage)
+#define EXPECT_JSON_VALID(configFileJson) expectJsonValid(configFileJson)
+#define EXPECT_JSON_INVALID(configFileJson, expectedErrorMessage, \
+ expectedOutputMessage) \
+ expectJsonInvalid(configFileJson, expectedErrorMessage, \
+ expectedOutputMessage)
+
+using json = nlohmann::json;
+
+const json validConfigFile = R"(
+ {
+ "comments": [ "Config file for a FooBar one-chassis system" ],
+
+ "rules": [
+ {
+ "comments": [ "Sets output voltage for a PMBus regulator rail" ],
+ "id": "set_voltage_rule",
+ "actions": [
+ {
+ "pmbus_write_vout_command": {
+ "format": "linear"
+ }
+ }
+ ]
+ }
+ ],
+
+ "chassis": [
+ {
+ "comments": [ "Chassis number 1 containing CPUs and memory" ],
+ "number": 1,
+ "devices": [
+ {
+ "comments": [ "IR35221 regulator producing the Vdd rail" ],
+ "id": "vdd_regulator",
+ "is_regulator": true,
+ "fru": "/system/chassis/motherboard/regulator1",
+ "i2c_interface": {
+ "bus": 1,
+ "address": "0x70"
+ },
+ "rails": [
+ {
+ "comments": [ "Vdd rail" ],
+ "id": "vdd",
+ "configuration": {
+ "volts": 1.03,
+ "rule_id": "set_voltage_rule"
+ },
+ "sensor_monitoring": {
+ "rule_id": "read_sensors_rule"
+ }
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+)"_json;
+
+std::string createTmpFile()
+{
+ // create temporary file using mkstemp under /tmp/. random name for XXXXXX
+ char fileName[] = "/tmp/temp-XXXXXX";
+ int fd = mkstemp(fileName);
+ if (fd == -1)
+ {
+ perror("Can't create temporary file");
+ }
+ close(fd);
+ return fileName;
+}
+
+std::string getValidationToolCommand(const std::string& configFileName)
+{
+ std::string command = "python ../tools/validate-regulators-config.py -s \
+ ../schema/config_schema.json -c ";
+ command += configFileName;
+ return command;
+}
+
+int runToolForOutput(const std::string& configFileName, std::string& output,
+ bool isReadingStderr = false)
+{
+ // run the validation tool with the temporary file and return the output
+ // of the validation tool.
+ std::string command = getValidationToolCommand(configFileName);
+ // reading the stderr while isReadingStderr is true.
+ if (isReadingStderr == true)
+ {
+ command += " 2>&1 >/dev/null";
+ }
+ // get the jsonschema print from validation tool.
+ char buffer[256];
+ std::string result = "";
+ // to get the stdout from the validation tool.
+ FILE* pipe = popen(command.c_str(), "r");
+ if (!pipe)
+ {
+ throw std::runtime_error("popen() failed!");
+ }
+ while (!std::feof(pipe))
+ {
+ if (fgets(buffer, sizeof buffer, pipe) != NULL)
+ {
+ result += buffer;
+ }
+ }
+ int returnValue = pclose(pipe);
+ // Check if pclose() failed
+ if (returnValue == -1)
+ {
+ // unable to close pipe. Print error and exit function.
+ throw std::runtime_error("pclose() failed!");
+ }
+ std::string firstLine = result.substr(0, result.find('\n'));
+ output = firstLine;
+ // Get command exit status from return value
+ int exitStatus = WEXITSTATUS(returnValue);
+ return exitStatus;
+}
+
+void expectFileValid(const std::string& configFileName)
+{
+ std::string errorMessage = "";
+ std::string outputMessage = "";
+ EXPECT_EQ(runToolForOutput(configFileName, errorMessage, true), 0);
+ EXPECT_EQ(runToolForOutput(configFileName, outputMessage), 0);
+ EXPECT_EQ(errorMessage, "");
+ EXPECT_EQ(outputMessage, "");
+}
+
+void expectFileInvalid(const std::string& configFileName,
+ const std::string& expectedErrorMessage,
+ const std::string& expectedOutputMessage)
+{
+ std::string errorMessage = "";
+ std::string outputMessage = "";
+ EXPECT_EQ(runToolForOutput(configFileName, errorMessage, true), 1);
+ EXPECT_EQ(runToolForOutput(configFileName, outputMessage), 1);
+ EXPECT_EQ(errorMessage, expectedErrorMessage);
+ EXPECT_EQ(outputMessage, expectedOutputMessage);
+}
+
+void expectJsonValid(const json configFileJson)
+{
+ std::string fileName;
+ fileName = createTmpFile();
+ std::string jsonData = configFileJson.dump();
+ std::ofstream out(fileName);
+ out << jsonData;
+ out.close();
+
+ EXPECT_FILE_VALID(fileName);
+ unlink(fileName.c_str());
+}
+
+void expectJsonInvalid(const json configFileJson,
+ const std::string& expectedErrorMessage,
+ const std::string& expectedOutputMessage)
+{
+ std::string fileName;
+ fileName = createTmpFile();
+ std::string jsonData = configFileJson.dump();
+ std::ofstream out(fileName);
+ out << jsonData;
+ out.close();
+
+ EXPECT_FILE_INVALID(fileName, expectedErrorMessage, expectedOutputMessage);
+ unlink(fileName.c_str());
+}
+
+TEST(ValidateRegulatorsConfigTest, Rule)
+{
+ // valid test comments property, id property,
+ // action property specified.
+ {
+ json configFile = validConfigFile;
+ EXPECT_JSON_VALID(configFile);
+ }
+
+ // valid test rule with no comments
+ {
+ json configFile = validConfigFile;
+ configFile["rules"][0].erase("comments");
+ EXPECT_JSON_VALID(configFile);
+ }
+
+ // invalid test comments property has invalid value type
+ {
+ json configFile = validConfigFile;
+ configFile["rules"][0]["comments"] = {1};
+ EXPECT_JSON_INVALID(configFile, "Validation failed.",
+ "1 is not of type u'string'");
+ }
+
+ // invalid test rule with no ID
+ {
+ json configFile = validConfigFile;
+ configFile["rules"][0].erase("id");
+ EXPECT_JSON_INVALID(configFile, "Validation failed.",
+ "u'id' is a required property");
+ }
+
+ // invalid test id property has invalid value type (not string)
+ {
+ json configFile = validConfigFile;
+ configFile["rules"][0]["id"] = true;
+ EXPECT_JSON_INVALID(configFile, "Validation failed.",
+ "True is not of type u'string'");
+ }
+
+ // invalid test id property has invalid value
+ {
+ json configFile = validConfigFile;
+ configFile["rules"][0]["id"] = "foo%";
+ EXPECT_JSON_INVALID(configFile, "Validation failed.",
+ "u'foo%' does not match u'^[A-Za-z0-9_]+$'");
+ }
+
+ // invalid test rule with no actions property
+ {
+ json configFile = validConfigFile;
+ configFile["rules"][0].erase("actions");
+ EXPECT_JSON_INVALID(configFile, "Validation failed.",
+ "u'actions' is a required property");
+ }
+
+ // valid test rule with multiple actions
+ {
+ json configFile = validConfigFile;
+ configFile["rules"][0]["actions"][1]["run_rule"] =
+ "set_page0_voltage_rule";
+ EXPECT_JSON_VALID(configFile);
+ }
+
+ // invalid test actions property has invalid value type (not an array)
+ {
+ json configFile = validConfigFile;
+ configFile["rules"][0]["actions"] = 1;
+ EXPECT_JSON_INVALID(configFile, "Validation failed.",
+ "1 is not of type u'array'");
+ }
+
+ // invalid test actions property has invalid value of action
+ {
+ json configFile = validConfigFile;
+ configFile["rules"][0]["actions"][0] = "foo";
+ EXPECT_JSON_INVALID(configFile, "Validation failed.",
+ "u'foo' is not of type u'object'");
+ }
+
+ // invalid test actions property has empty array
+ {
+ json configFile = validConfigFile;
+ configFile["rules"][0]["actions"] = json::array();
+ EXPECT_JSON_INVALID(configFile, "Validation failed.",
+ "[] is too short");
+ }
+}
\ No newline at end of file