|  | // A basic unit test that runs on a BMC (qemu or hardware) | 
|  |  | 
|  | #include <getopt.h> | 
|  | #include <iostream> | 
|  | #include <systemd/sd-journal.h> | 
|  | #include <sstream> | 
|  | #include <sdbusplus/exception.hpp> | 
|  | #include <phosphor-logging/elog.hpp> | 
|  | #include <phosphor-logging/log.hpp> | 
|  | #include <phosphor-logging/elog-errors.hpp> | 
|  |  | 
|  | using namespace phosphor; | 
|  | using namespace logging; | 
|  |  | 
|  | const char *usage = "Usage: logging-test [OPTION]          \n\n\ | 
|  | Options:                                                     \n\ | 
|  | [NONE]                          Default test case.           \n\ | 
|  | -h, --help                      Display this usage text.     \n\ | 
|  | -c, --commit <string>           Commit desired error.      \n\n\ | 
|  | Valid errors to commit:                                      \n\ | 
|  | AutoTestSimple\n"; | 
|  |  | 
|  | // validate the journal metadata equals the input value | 
|  | int validate_journal(const char *i_entry, const char *i_value) | 
|  | { | 
|  | sd_journal *journal; | 
|  | const void *data; | 
|  | size_t l; | 
|  | int rc; | 
|  | bool validated = false; | 
|  |  | 
|  | rc = sd_journal_open(&journal, SD_JOURNAL_LOCAL_ONLY); | 
|  | if (rc < 0) { | 
|  | std::cerr << "Failed to open journal: " << strerror(-rc) << "\n"; | 
|  | return 1; | 
|  | } | 
|  | rc = sd_journal_query_unique(journal, i_entry); | 
|  | if (rc < 0) { | 
|  | std::cerr << "Failed to query journal: " << strerror(-rc) << "\n"; | 
|  | return 1; | 
|  | } | 
|  | SD_JOURNAL_FOREACH_UNIQUE(journal, data, l) | 
|  | { | 
|  | std::string journ_entry((const char*)data); | 
|  | std::cout << journ_entry << "\n"; | 
|  | if(journ_entry.find(i_value) != std::string::npos) | 
|  | { | 
|  | std::cout << "We found it!\n"; | 
|  | validated = true; | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | sd_journal_close(journal); | 
|  |  | 
|  | rc = (validated) ? 0 : 1; | 
|  | if(rc) | 
|  | { | 
|  | std::cerr << "Failed to find " << i_entry << " with value " << i_value | 
|  | <<" in journal!" << "\n"; | 
|  | } | 
|  |  | 
|  | return rc; | 
|  | } | 
|  |  | 
|  | int elog_test() | 
|  | { | 
|  | // TEST 1 - Basic log | 
|  | log<level::DEBUG>("Basic phosphor logging test"); | 
|  |  | 
|  | // TEST 2 - Log with metadata field | 
|  | const char *file_name = "phosphor_logging_test.txt"; | 
|  | int number = 0xFEFE; | 
|  | log<level::DEBUG>("phosphor logging test with attribute", | 
|  | entry("FILE_NAME_WITH_NUM_TEST=%s_%x", file_name, number)); | 
|  |  | 
|  | // Now read back and verify our data made it into the journal | 
|  | int rc = validate_journal("FILE_NAME_WITH_NUM_TEST", | 
|  | "phosphor_logging_test.txt_fefe"); | 
|  | if(rc) | 
|  | return(rc); | 
|  |  | 
|  | // TEST 3 - Create error log with 2 meta data fields (rvalue and lvalue) | 
|  | number = 0x1234; | 
|  | const char *test_string = "/tmp/test_string/"; | 
|  | try | 
|  | { | 
|  | elog<example::xyz::openbmc_project::Example::Elog::TestErrorOne>( | 
|  | example::xyz::openbmc_project::Example::Elog:: | 
|  | TestErrorOne::ERRNUM(number), | 
|  | example::xyz::openbmc_project::Example::Elog:: | 
|  | TestErrorOne::FILE_PATH(test_string), | 
|  | example::xyz::openbmc_project::Example::Elog:: | 
|  | TestErrorOne::FILE_NAME("elog_test_3.txt"), | 
|  | example::xyz::openbmc_project::Example::Elog:: | 
|  | TestErrorTwo::DEV_ADDR(0xDEADDEAD), | 
|  | example::xyz::openbmc_project::Example::Elog:: | 
|  | TestErrorTwo::DEV_ID(100), | 
|  | example::xyz::openbmc_project::Example::Elog:: | 
|  | TestErrorTwo::DEV_NAME("test case 3")); | 
|  | } | 
|  | catch (example::xyz::openbmc_project::Example::Elog::TestErrorOne& e) | 
|  | { | 
|  | std::cout << "elog exception caught: " << e.what() << std::endl; | 
|  | } | 
|  |  | 
|  | // Reduce our error namespaces | 
|  | using namespace example::xyz::openbmc_project::Example::Elog; | 
|  |  | 
|  | // Now read back and verify our data made it into the journal | 
|  | std::stringstream stream; | 
|  | stream << std::hex << number; | 
|  | rc = validate_journal(TestErrorOne::ERRNUM::str_short, | 
|  | std::string(stream.str()).c_str()); | 
|  | if(rc) | 
|  | return(rc); | 
|  |  | 
|  | rc = validate_journal(TestErrorOne::FILE_PATH::str_short, | 
|  | test_string); | 
|  | if(rc) | 
|  | return(rc); | 
|  |  | 
|  | rc = validate_journal(TestErrorOne::FILE_NAME::str_short, | 
|  | "elog_test_3.txt"); | 
|  | if(rc) | 
|  | return(rc); | 
|  |  | 
|  | rc = validate_journal(TestErrorTwo::DEV_ADDR::str_short, | 
|  | "0xDEADDEAD"); | 
|  | if(rc) | 
|  | return(rc); | 
|  |  | 
|  | rc = validate_journal(TestErrorTwo::DEV_ID::str_short, | 
|  | "100"); | 
|  | if(rc) | 
|  | return(rc); | 
|  |  | 
|  | rc = validate_journal(TestErrorTwo::DEV_NAME::str_short, | 
|  | "test case 3"); | 
|  | if(rc) | 
|  | return(rc); | 
|  |  | 
|  | // TEST 4 - Create error log with previous entry use | 
|  | number = 0x9876; | 
|  | try | 
|  | { | 
|  | elog<TestErrorOne>(TestErrorOne::ERRNUM(number), | 
|  | prev_entry<TestErrorOne::FILE_PATH>(), | 
|  | TestErrorOne::FILE_NAME("elog_test_4.txt"), | 
|  | TestErrorTwo::DEV_ADDR(0xDEADDEAD), | 
|  | TestErrorTwo::DEV_ID(100), | 
|  | TestErrorTwo::DEV_NAME("test case 4")); | 
|  | } | 
|  | catch (sdbusplus::exception_t& e) | 
|  | { | 
|  | std::cout << "elog exception caught: " << e.what() << std::endl; | 
|  | } | 
|  |  | 
|  | // Now read back and verify our data made it into the journal | 
|  | stream.str(""); | 
|  | stream << std::hex << number; | 
|  | rc = validate_journal(TestErrorOne::ERRNUM::str_short, | 
|  | std::string(stream.str()).c_str()); | 
|  | if(rc) | 
|  | return(rc); | 
|  |  | 
|  | // This should just be equal to what we put in test 3 | 
|  | rc = validate_journal(TestErrorOne::FILE_PATH::str_short, | 
|  | test_string); | 
|  | if(rc) | 
|  | return(rc); | 
|  |  | 
|  | rc = validate_journal(TestErrorOne::FILE_NAME::str_short, | 
|  | "elog_test_4.txt"); | 
|  | if(rc) | 
|  | return(rc); | 
|  |  | 
|  | rc = validate_journal(TestErrorTwo::DEV_ADDR::str_short, | 
|  | "0xDEADDEAD"); | 
|  | if(rc) | 
|  | return(rc); | 
|  |  | 
|  | rc = validate_journal(TestErrorTwo::DEV_ID::str_short, | 
|  | "100"); | 
|  | if(rc) | 
|  | return(rc); | 
|  |  | 
|  | rc = validate_journal(TestErrorTwo::DEV_NAME::str_short, | 
|  | "test case 4"); | 
|  | if(rc) | 
|  | return(rc); | 
|  |  | 
|  | // Compile fail tests | 
|  |  | 
|  | // Simple test to prove we fail to compile due to missing param | 
|  | //elog<TestErrorOne>(TestErrorOne::ERRNUM(1), | 
|  | //                   TestErrorOne::FILE_PATH("test")); | 
|  |  | 
|  | // Simple test to prove we fail to compile due to invalid param | 
|  | //elog<TestErrorOne>(TestErrorOne::ERRNUM(1), | 
|  | //                   TestErrorOne::FILE_PATH("test"), | 
|  | //                   TestErrorOne::FILE_NAME(1)); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | void commitError(const char *text) | 
|  | { | 
|  | if (strcmp(text, "AutoTestSimple") == 0) | 
|  | { | 
|  | try | 
|  | { | 
|  | elog<example::xyz::openbmc_project::Example::Elog:: | 
|  | AutoTestSimple>( | 
|  | example::xyz::openbmc_project::Example::Elog:: | 
|  | AutoTestSimple::STRING("FOO")); | 
|  | } | 
|  | catch (example::xyz::openbmc_project::Example::Elog:: | 
|  | AutoTestSimple& e) | 
|  | { | 
|  | std::cout << "elog exception caught: " << e.what() << std::endl; | 
|  | commit(e.name()); | 
|  | } | 
|  | } | 
|  |  | 
|  | return; | 
|  | } | 
|  |  | 
|  | int main(int argc, char *argv[]) | 
|  | { | 
|  | char arg; | 
|  |  | 
|  | if (argc == 1) | 
|  | return elog_test(); | 
|  |  | 
|  | static struct option long_options[] = | 
|  | { | 
|  | {"help",    no_argument,       0, 'h'}, | 
|  | {"commit",  required_argument, 0, 'c'}, | 
|  | {0, 0, 0, 0} | 
|  | }; | 
|  | int option_index = 0; | 
|  |  | 
|  | while((arg=getopt_long(argc,argv,"hc:",long_options,&option_index)) != -1) | 
|  | { | 
|  | switch (arg) | 
|  | { | 
|  | case 'c': | 
|  | commitError(optarg); | 
|  | return 0; | 
|  |  | 
|  | case 'h': | 
|  | case '?': | 
|  | std::cerr << usage; | 
|  | return 1; | 
|  |  | 
|  | } | 
|  | } | 
|  |  | 
|  | return 0; | 
|  |  | 
|  | } |