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