1 // A basic unit test that runs on a BMC (qemu or hardware)
2 
3 #include <getopt.h>
4 #include <systemd/sd-journal.h>
5 
6 #include <cstring>
7 #include <iostream>
8 #include <phosphor-logging/elog-errors.hpp>
9 #include <phosphor-logging/elog.hpp>
10 #include <phosphor-logging/log.hpp>
11 #include <sdbusplus/exception.hpp>
12 #include <sstream>
13 
14 using namespace phosphor;
15 using namespace logging;
16 
17 const char* usage = "Usage: logging-test [OPTION]          \n\n\
18 Options:                                                     \n\
19 [NONE]                          Default test case.           \n\
20 -h, --help                      Display this usage text.     \n\
21 -c, --commit <string>           Commit desired error.      \n\n\
22 Valid errors to commit:                                      \n\
23 AutoTestSimple, AutoTestCreateAndCommit\n";
24 
25 // validate the journal metadata equals the input value
26 int validate_journal(const char* i_entry, const char* i_value)
27 {
28     sd_journal* journal;
29     const void* data;
30     size_t l;
31     int rc;
32     bool validated = false;
33 
34     rc = sd_journal_open(&journal, SD_JOURNAL_LOCAL_ONLY);
35     if (rc < 0)
36     {
37         std::cerr << "Failed to open journal: " << strerror(-rc) << "\n";
38         return 1;
39     }
40     rc = sd_journal_query_unique(journal, i_entry);
41     if (rc < 0)
42     {
43         std::cerr << "Failed to query journal: " << strerror(-rc) << "\n";
44         return 1;
45     }
46     SD_JOURNAL_FOREACH_UNIQUE(journal, data, l)
47     {
48         std::string journ_entry((const char*)data);
49         std::cout << journ_entry << "\n";
50         if (journ_entry.find(i_value) != std::string::npos)
51         {
52             std::cout << "We found it!\n";
53             validated = true;
54             break;
55         }
56     }
57 
58     sd_journal_close(journal);
59 
60     rc = (validated) ? 0 : 1;
61     if (rc)
62     {
63         std::cerr << "Failed to find " << i_entry << " with value " << i_value
64                   << " in journal!"
65                   << "\n";
66     }
67 
68     return rc;
69 }
70 
71 int elog_test()
72 {
73     // TEST 1 - Basic log
74     log<level::DEBUG>("Basic phosphor logging test");
75 
76     // TEST 2 - Log with metadata field
77     const char* file_name = "phosphor_logging_test.txt";
78     int number = 0xFEFE;
79     log<level::DEBUG>(
80         "phosphor logging test with attribute",
81         entry("FILE_NAME_WITH_NUM_TEST=%s_%x", file_name, number));
82 
83     // Now read back and verify our data made it into the journal
84     int rc = validate_journal("FILE_NAME_WITH_NUM_TEST",
85                               "phosphor_logging_test.txt_fefe");
86     if (rc)
87         return (rc);
88 
89     // TEST 3 - Create error log with 2 meta data fields (rvalue and lvalue)
90     number = 0x1234;
91     const char* test_string = "/tmp/test_string/";
92     try
93     {
94         elog<example::xyz::openbmc_project::Example::Elog::TestErrorOne>(
95             example::xyz::openbmc_project::Example::Elog::TestErrorOne::ERRNUM(
96                 number),
97             example::xyz::openbmc_project::Example::Elog::TestErrorOne::
98                 FILE_PATH(test_string),
99             example::xyz::openbmc_project::Example::Elog::TestErrorOne::
100                 FILE_NAME("elog_test_3.txt"),
101             example::xyz::openbmc_project::Example::Elog::TestErrorTwo::
102                 DEV_ADDR(0xDEADDEAD),
103             example::xyz::openbmc_project::Example::Elog::TestErrorTwo::DEV_ID(
104                 100),
105             example::xyz::openbmc_project::Example::Elog::TestErrorTwo::
106                 DEV_NAME("test case 3"));
107     }
108     catch (example::xyz::openbmc_project::Example::Elog::TestErrorOne& e)
109     {
110         std::cout << "elog exception caught: " << e.what() << std::endl;
111     }
112 
113     // Reduce our error namespaces
114     using namespace example::xyz::openbmc_project::Example::Elog;
115 
116     // Now read back and verify our data made it into the journal
117     std::stringstream stream;
118     stream << std::hex << number;
119     rc = validate_journal(TestErrorOne::ERRNUM::str_short,
120                           std::string(stream.str()).c_str());
121     if (rc)
122         return (rc);
123 
124     rc = validate_journal(TestErrorOne::FILE_PATH::str_short, test_string);
125     if (rc)
126         return (rc);
127 
128     rc =
129         validate_journal(TestErrorOne::FILE_NAME::str_short, "elog_test_3.txt");
130     if (rc)
131         return (rc);
132 
133     rc = validate_journal(TestErrorTwo::DEV_ADDR::str_short, "0xDEADDEAD");
134     if (rc)
135         return (rc);
136 
137     rc = validate_journal(TestErrorTwo::DEV_ID::str_short, "100");
138     if (rc)
139         return (rc);
140 
141     rc = validate_journal(TestErrorTwo::DEV_NAME::str_short, "test case 3");
142     if (rc)
143         return (rc);
144 
145     // TEST 4 - Create error log with previous entry use
146     number = 0x9876;
147     try
148     {
149         elog<TestErrorOne>(
150             TestErrorOne::ERRNUM(number), prev_entry<TestErrorOne::FILE_PATH>(),
151             TestErrorOne::FILE_NAME("elog_test_4.txt"),
152             TestErrorTwo::DEV_ADDR(0xDEADDEAD), TestErrorTwo::DEV_ID(100),
153             TestErrorTwo::DEV_NAME("test case 4"));
154     }
155     catch (sdbusplus::exception_t& e)
156     {
157         std::cout << "elog exception caught: " << e.what() << std::endl;
158     }
159 
160     // Now read back and verify our data made it into the journal
161     stream.str("");
162     stream << std::hex << number;
163     rc = validate_journal(TestErrorOne::ERRNUM::str_short,
164                           std::string(stream.str()).c_str());
165     if (rc)
166         return (rc);
167 
168     // This should just be equal to what we put in test 3
169     rc = validate_journal(TestErrorOne::FILE_PATH::str_short, test_string);
170     if (rc)
171         return (rc);
172 
173     rc =
174         validate_journal(TestErrorOne::FILE_NAME::str_short, "elog_test_4.txt");
175     if (rc)
176         return (rc);
177 
178     rc = validate_journal(TestErrorTwo::DEV_ADDR::str_short, "0xDEADDEAD");
179     if (rc)
180         return (rc);
181 
182     rc = validate_journal(TestErrorTwo::DEV_ID::str_short, "100");
183     if (rc)
184         return (rc);
185 
186     rc = validate_journal(TestErrorTwo::DEV_NAME::str_short, "test case 4");
187     if (rc)
188         return (rc);
189 
190     // Compile fail tests
191 
192     // Simple test to prove we fail to compile due to missing param
193     // elog<TestErrorOne>(TestErrorOne::ERRNUM(1),
194     //                   TestErrorOne::FILE_PATH("test"));
195 
196     // Simple test to prove we fail to compile due to invalid param
197     // elog<TestErrorOne>(TestErrorOne::ERRNUM(1),
198     //                   TestErrorOne::FILE_PATH("test"),
199     //                   TestErrorOne::FILE_NAME(1));
200 
201     return 0;
202 }
203 
204 void commitError(const char* text)
205 {
206     if (std::strcmp(text, "AutoTestSimple") == 0)
207     {
208         try
209         {
210             elog<example::xyz::openbmc_project::Example::Elog::AutoTestSimple>(
211                 example::xyz::openbmc_project::Example::Elog::AutoTestSimple::
212                     STRING("FOO"));
213         }
214         catch (example::xyz::openbmc_project::Example::Elog::AutoTestSimple& e)
215         {
216             std::cout << "elog exception caught: " << e.what() << std::endl;
217             commit(e.name());
218         }
219     }
220     else if (std::strcmp(text, "AutoTestCreateAndCommit") == 0)
221     {
222         report<example::xyz::openbmc_project::Example::Elog::AutoTestSimple>(
223             example::xyz::openbmc_project::Example::Elog::AutoTestSimple::
224                 STRING("FOO"));
225     }
226 
227     return;
228 }
229 
230 int main(int argc, char* argv[])
231 {
232     int arg;
233 
234     if (argc == 1)
235         return elog_test();
236 
237     static struct option long_options[] = {
238         {"help", no_argument, 0, 'h'},
239         {"commit", required_argument, 0, 'c'},
240         {0, 0, 0, 0}};
241     int option_index = 0;
242 
243     while ((arg = getopt_long(argc, argv, "hc:", long_options,
244                               &option_index)) != -1)
245     {
246         switch (arg)
247         {
248             case 'c':
249                 commitError(optarg);
250                 return 0;
251             case 'h':
252             case '?':
253                 std::cerr << usage;
254                 return 1;
255         }
256     }
257 
258     return 0;
259 }
260