1 #include <systemd_target_parser.hpp>
2 
3 #include <cstdio>
4 #include <cstdlib>
5 #include <filesystem>
6 #include <iostream>
7 
8 #include <gtest/gtest.h>
9 
10 namespace fs = std::filesystem;
11 
12 // Enable debug by default for debug when needed
13 bool gVerbose = true;
14 
TEST(TargetJsonParser,BasicGoodPath)15 TEST(TargetJsonParser, BasicGoodPath)
16 {
17     auto defaultData1 = R"(
18         {
19             "targets" : {
20                 "multi-user.target" : {
21                     "errorsToMonitor": ["default"],
22                     "errorToLog": "xyz.openbmc_project.State.BMC.Error.MultiUserTargetFailure"},
23                 "obmc-chassis-poweron@0.target" : {
24                     "errorsToMonitor": ["timeout", "failed"],
25                     "errorToLog": "xyz.openbmc_project.State.Chassis.Error.PowerOnTargetFailure"}
26                 }
27         }
28     )"_json;
29 
30     auto defaultData2 = R"(
31         {
32             "targets" : {
33                 "obmc-host-start@0.target" : {
34                     "errorsToMonitor": ["default"],
35                     "errorToLog": "xyz.openbmc_project.State.Host.Error.HostStartFailure"},
36                 "obmc-host-stop@0.target" : {
37                     "errorsToMonitor": ["dependency"],
38                     "errorToLog": "xyz.openbmc_project.State.Host.Error.HostStopFailure"}
39                 }
40         }
41     )"_json;
42 
43     std::FILE* tmpf = fopen("/tmp/good_file1.json", "w");
44     std::fputs(defaultData1.dump().c_str(), tmpf);
45     std::fclose(tmpf);
46 
47     tmpf = fopen("/tmp/good_file2.json", "w");
48     std::fputs(defaultData2.dump().c_str(), tmpf);
49     std::fclose(tmpf);
50 
51     std::vector<std::string> filePaths;
52     filePaths.emplace_back("/tmp/good_file1.json");
53     filePaths.emplace_back("/tmp/good_file2.json");
54 
55     TargetErrorData targetData = parseFiles(filePaths);
56 
57     EXPECT_EQ(targetData.size(), 4);
58     EXPECT_NE(targetData.find("multi-user.target"), targetData.end());
59     EXPECT_NE(targetData.find("obmc-chassis-poweron@0.target"),
60               targetData.end());
61     EXPECT_NE(targetData.find("obmc-host-start@0.target"), targetData.end());
62     EXPECT_NE(targetData.find("obmc-host-stop@0.target"), targetData.end());
63     targetEntry tgt = targetData["obmc-chassis-poweron@0.target"];
64     EXPECT_EQ(tgt.errorToLog,
65               "xyz.openbmc_project.State.Chassis.Error.PowerOnTargetFailure");
66     EXPECT_EQ(tgt.errorsToMonitor.size(), 2);
67     // Check a target with "default" for errorsToMonitor, should have 3 defaults
68     tgt = targetData["obmc-host-start@0.target"];
69     EXPECT_EQ(tgt.errorsToMonitor.size(), 3);
70 
71     std::remove("/tmp/good_file1.json");
72     std::remove("/tmp/good_file2.json");
73 }
74 
TEST(TargetJsonParser,InvalidErrorToMonitor)75 TEST(TargetJsonParser, InvalidErrorToMonitor)
76 {
77     auto invalidDataError = R"(
78         {
79             "targets" : {
80                 "obmc-chassis-poweron@0.target" : {
81                     "errorsToMonitor": ["timeout", "invalid"],
82                     "errorToLog": "xyz.openbmc_project.State.Chassis.Error.PowerOnTargetFailure"}
83                 }
84         }
85     )"_json;
86 
87     std::FILE* tmpf = fopen("/tmp/invalid_error_file.json", "w");
88     std::fputs(invalidDataError.dump().c_str(), tmpf);
89     std::fclose(tmpf);
90 
91     std::vector<std::string> filePaths;
92     filePaths.emplace_back("/tmp/invalid_error_file.json");
93 
94     // Verify exception thrown on invalid errorsToMonitor
95     EXPECT_THROW(TargetErrorData targetData = parseFiles(filePaths),
96                  std::out_of_range);
97     std::remove("/tmp/invalid_error_file.json");
98 }
99 
TEST(TargetJsonParser,InvalidFileFormat)100 TEST(TargetJsonParser, InvalidFileFormat)
101 {
102     std::FILE* tmpf = fopen("/tmp/invalid_json_file.json", "w");
103     std::fputs(R"({"targets":{"missing closing quote}})", tmpf);
104     fclose(tmpf);
105 
106     std::vector<std::string> filePaths;
107     filePaths.emplace_back("/tmp/invalid_json_file.json");
108 
109     // Verify exception thrown on invalid json file format
110     EXPECT_THROW(TargetErrorData targetData = parseFiles(filePaths),
111                  nlohmann::detail::parse_error);
112     std::remove("/tmp/invalid_json_file.json");
113 }
114 
TEST(TargetJsonParser,NotJustDefault)115 TEST(TargetJsonParser, NotJustDefault)
116 {
117     auto notJustDefault = R"(
118         {
119             "targets" : {
120                 "obmc-chassis-poweron@0.target" : {
121                     "errorsToMonitor": ["timeout", "default"],
122                     "errorToLog": "xyz.openbmc_project.State.Chassis.Error.PowerOnTargetFailure"}
123                 }
124         }
125     )"_json;
126 
127     std::FILE* tmpf = fopen("/tmp/not_just_default_file.json", "w");
128     std::fputs(notJustDefault.dump().c_str(), tmpf);
129     std::fclose(tmpf);
130 
131     std::vector<std::string> filePaths;
132     filePaths.emplace_back("/tmp/not_just_default_file.json");
133 
134     // Verify exception thrown on invalid errorsToMonitor
135     EXPECT_THROW(TargetErrorData targetData = parseFiles(filePaths),
136                  std::invalid_argument);
137     std::remove("/tmp/not_just_default_file.json");
138 }
139