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