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