sysd_monitor: Parse json file(s)

Parse the json file(s) into a c++ data object that can be used in later
commits to easily check for monitored targets and log appropriate
errors.

Accept a command line parameter to input file and call parsing function

Tested:
- Verified 100% code coverage of new parser cpp

Change-Id: I0bacd80b7f5330eb9cb03d8e3717742ab107bf94
Signed-off-by: Andrew Geissler <geissonator@yahoo.com>
diff --git a/test/systemd_parser.cpp b/test/systemd_parser.cpp
new file mode 100644
index 0000000..7ca94b9
--- /dev/null
+++ b/test/systemd_parser.cpp
@@ -0,0 +1,109 @@
+#include <systemd_target_parser.hpp>
+#include <gtest/gtest.h>
+
+#include <iostream>
+#include <cstdio>
+#include <cstdlib>
+#include <filesystem>
+
+namespace fs = std::filesystem;
+
+// Enable debug by default for debug when needed
+bool gVerbose = true;
+
+TEST(TargetJsonParser, BasicGoodPath)
+{
+    auto defaultData1 = R"(
+        {
+            "targets" : {
+                "multi-user.target" : {
+                    "errorsToMonitor": ["default"],
+                    "errorToLog": "xyz.openbmc_project.State.BMC.Error.MultiUserTargetFailure"},
+                "obmc-chassis-poweron@0.target" : {
+                    "errorsToMonitor": ["timeout", "failed"],
+                    "errorToLog": "xyz.openbmc_project.State.Chassis.Error.PowerOnTargetFailure"}
+                }
+        }
+    )"_json;
+
+    auto defaultData2 = R"(
+        {
+            "targets" : {
+                "obmc-host-start@0.target" : {
+                    "errorsToMonitor": ["default"],
+                    "errorToLog": "xyz.openbmc_project.State.Host.Error.HostStartFailure"},
+                "obmc-host-stop@0.target" : {
+                    "errorsToMonitor": ["dependency"],
+                    "errorToLog": "xyz.openbmc_project.State.Host.Error.HostStopFailure"}
+                }
+        }
+    )"_json;
+
+    std::FILE* tmpf = fopen("/tmp/good_file1.json", "w");
+    std::fputs(defaultData1.dump().c_str(), tmpf);
+    std::fclose(tmpf);
+
+    tmpf = fopen("/tmp/good_file2.json", "w");
+    std::fputs(defaultData2.dump().c_str(), tmpf);
+    std::fclose(tmpf);
+
+    std::vector<std::string> filePaths;
+    filePaths.push_back("/tmp/good_file1.json");
+    filePaths.push_back("/tmp/good_file2.json");
+
+    TargetErrorData targetData = parseFiles(filePaths);
+
+    EXPECT_EQ(targetData.size(), 4);
+    EXPECT_NE(targetData.find("multi-user.target"), targetData.end());
+    EXPECT_NE(targetData.find("obmc-chassis-poweron@0.target"),
+              targetData.end());
+    EXPECT_NE(targetData.find("obmc-host-start@0.target"), targetData.end());
+    EXPECT_NE(targetData.find("obmc-host-stop@0.target"), targetData.end());
+    targetEntry tgt = targetData["obmc-chassis-poweron@0.target"];
+    EXPECT_EQ(tgt.errorToLog,
+              "xyz.openbmc_project.State.Chassis.Error.PowerOnTargetFailure");
+    EXPECT_EQ(tgt.errorsToMonitor.size(), 2);
+
+    std::remove("/tmp/good_file1.json");
+    std::remove("/tmp/good_file2.json");
+}
+
+TEST(TargetJsonParser, InvalidErrorToMonitor)
+{
+    auto invalidDataError = R"(
+        {
+            "targets" : {
+                "obmc-chassis-poweron@0.target" : {
+                    "errorsToMonitor": ["timeout", "invalid"],
+                    "errorToLog": "xyz.openbmc_project.State.Chassis.Error.PowerOnTargetFailure"}
+                }
+        }
+    )"_json;
+
+    std::FILE* tmpf = fopen("/tmp/invalid_error_file.json", "w");
+    std::fputs(invalidDataError.dump().c_str(), tmpf);
+    std::fclose(tmpf);
+
+    std::vector<std::string> filePaths;
+    filePaths.push_back("/tmp/invalid_error_file.json");
+
+    // Verify exception thrown on invalid errorsToMonitor
+    EXPECT_THROW(TargetErrorData targetData = parseFiles(filePaths),
+                 std::out_of_range);
+    std::remove("/tmp/invalid_error_file.json");
+}
+
+TEST(TargetJsonParser, InvalidFileFormat)
+{
+    std::FILE* tmpf = fopen("/tmp/invalid_json_file.json", "w");
+    std::fputs("{\"targets\":{\"missing closing quote}}", tmpf);
+    fclose(tmpf);
+
+    std::vector<std::string> filePaths;
+    filePaths.push_back("/tmp/invalid_json_file.json");
+
+    // Verify exception thrown on invalid json file format
+    EXPECT_THROW(TargetErrorData targetData = parseFiles(filePaths),
+                 nlohmann::detail::parse_error);
+    std::remove("/tmp/invalid_json_file.json");
+}