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