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