Add in test case which validates elog client interface

Change-Id: I5c5f7a550a8f272251893ff616408c41d32281f9
Signed-off-by: Andrew Geissler <andrewg@us.ibm.com>
diff --git a/Makefile.am b/Makefile.am
index 0dd561a..59963f8 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1,8 +1,11 @@
+# export these headers
+include_HEADERS = log.hpp elog.hpp
+
+# Application test which runs in obmc env (qemu, real hardware)
 bin_PROGRAMS = logging-test
-logging_test_SOURCES = test/logging_test.cpp
+logging_test_SOURCES = logging_test.cpp
 
 # systemd required for journal interfaces
 logging_test_LDFLAGS = $(SYSTEMD_LIBS)
 
-# export these headers
-include_HEADERS = log.hpp elog.hpp
\ No newline at end of file
+SUBDIRS = test
\ No newline at end of file
diff --git a/bootstrap.sh b/bootstrap.sh
new file mode 100755
index 0000000..9941c73
--- /dev/null
+++ b/bootstrap.sh
@@ -0,0 +1,18 @@
+#!/bin/sh -xe
+
+AUTOCONF_FILES="Makefile.in aclocal.m4 ar-lib autom4te.cache compile \
+        config.guess config.h.in config.sub configure depcomp install-sh \
+        ltmain.sh missing *libtool test-driver"
+
+case $1 in
+    clean)
+        test -f Makefile && make maintainer-clean
+        for file in ${AUTOCONF_FILES}; do
+            find -name "$file" | xargs -r rm -rf
+        done
+        exit 0
+        ;;
+esac
+
+autoreconf -i
+echo 'Run "./configure ${CONFIGURE_FLAGS} && make"'
\ No newline at end of file
diff --git a/configure.ac b/configure.ac
index 3f01c2e..7f8a446 100644
--- a/configure.ac
+++ b/configure.ac
@@ -20,6 +20,29 @@
 AC_CHECK_HEADER(systemd/sd-journal.h, ,[AC_MSG_ERROR([Could not find \
 systemd/sd-journal.h...systemd developement package required])])
 
+# Check/set gtest specific functions.
+AX_PTHREAD([GTEST_CPPFLAGS="-DGTEST_HAS_PTHREAD=1"],[GTEST_CPPFLAGS="-DGTEST_HAS_PTHREAD=0"])
+AC_SUBST(GTEST_CPPFLAGS)
+
+# Test cases require SDK so only build if we're told to (and SDK is available)
+AC_ARG_ENABLE([oe-sdk],
+    AS_HELP_STRING([--enable-oe-sdk], [Link testcases absolutely against OE SDK so they can be ran within it.])
+)
+AC_ARG_VAR(OECORE_TARGET_SYSROOT,
+    [Path to the OE SDK SYSROOT])
+AS_IF([test "x$enable_oe_sdk" == "xyes"],
+    AS_IF([test "x$OECORE_TARGET_SYSROOT" == "x"],
+          AC_MSG_ERROR([OECORE_TARGET_SYSROOT must be set with --enable-oe-sdk])
+    )
+    AC_MSG_NOTICE([Enabling OE-SDK at $OECORE_TARGET_SYSROOT])
+    [
+        testcase_flags="-Wl,-rpath,\${OECORE_TARGET_SYSROOT}/lib"
+        testcase_flags="${testcase_flags} -Wl,-rpath,\${OECORE_TARGET_SYSROOT}/usr/lib"
+        testcase_flags="${testcase_flags} -Wl,-dynamic-linker,`find \${OECORE_TARGET_SYSROOT}/lib/ld-*.so | sort -r -n | head -n1`"
+    ]
+    AC_SUBST([OESDK_TESTCASE_FLAGS], [$testcase_flags])
+)
+
 AC_CONFIG_HEADERS([config.h])
-AC_CONFIG_FILES([Makefile])
+AC_CONFIG_FILES([Makefile test/Makefile])
 AC_OUTPUT
\ No newline at end of file
diff --git a/elog-gen.hpp b/elog-gen.hpp
index 52d8d9b..3aba8fb 100644
--- a/elog-gen.hpp
+++ b/elog-gen.hpp
@@ -16,6 +16,7 @@
 struct errnum
 {
     static constexpr auto str = "ERRNO=%d";
+    static constexpr auto str_short = "ERRNO";
     using type = std::tuple<std::decay_t<decltype(str)>,int>;
     explicit constexpr errnum(int a) : _entry(entry(str, a)) {};
     type _entry;
@@ -24,6 +25,7 @@
 struct file_path
 {
     static constexpr auto str = "FILE_PATH=%s";
+    static constexpr auto str_short = "FILE_PATH";
     using type = std::tuple<std::decay_t<decltype(str)>,const char*>;
     explicit constexpr file_path(const char *a) : _entry(entry(str,a)) {};
     type _entry;
@@ -32,6 +34,7 @@
 struct file_name
 {
    static constexpr auto str = "FILE_NAME=%s";
+   static constexpr auto str_short = "FILE_NAME";
    using type = std::tuple<std::decay_t<decltype(str)>,const char*>;
    explicit constexpr file_name(const char *a) : _entry(entry(str,a)) {};
    type _entry;
diff --git a/log.hpp b/log.hpp
index cba6d37..1f1031c 100644
--- a/log.hpp
+++ b/log.hpp
@@ -95,6 +95,7 @@
 template <typename T, size_t ...I>
 void helper_log(T&& e, std::integer_sequence<size_t, I...>)
 {
+    // https://www.freedesktop.org/software/systemd/man/sd_journal_print.html
     sd_journal_send(std::get<I>(std::forward<T>(e))..., NULL);
 }
 
diff --git a/logging_test.cpp b/logging_test.cpp
new file mode 100644
index 0000000..5f153c1
--- /dev/null
+++ b/logging_test.cpp
@@ -0,0 +1,130 @@
+// A basic unit test that runs on a BMC (qemu or hardware)
+
+#include <iostream>
+#include <elog.hpp>
+#include <log.hpp>
+#include <systemd/sd-journal.h>
+
+using namespace phosphor;
+using namespace logging;
+
+// 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 << " in journal!" << "\n";
+    }
+
+    return rc;
+}
+
+int main()
+{
+    // 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/";
+    elog<file_not_found>(file_not_found::errnum(number),
+                         file_not_found::file_path(test_string),
+                         file_not_found::file_name("elog_test_3.txt"));
+
+    // Now read back and verify our data made it into the journal
+    rc = validate_journal(file_not_found::errnum::str_short,
+                          std::to_string(number).c_str());
+    if(rc)
+        return(rc);
+
+    rc = validate_journal(file_not_found::file_path::str_short,
+                          test_string);
+    if(rc)
+        return(rc);
+
+    rc = validate_journal(file_not_found::file_name::str_short,
+                          "elog_test_3.txt");
+    if(rc)
+        return(rc);
+
+    // TEST 4 - Create error log with previous entry use
+    number = 0xFEDC;
+    elog<file_not_found>(file_not_found::errnum(number),
+                         prev_entry<file_not_found::file_path>(),
+                         file_not_found::file_name("elog_test_4.txt"));
+
+    // Now read back and verify our data made it into the journal
+    rc = validate_journal(file_not_found::errnum::str_short,
+                          std::to_string(number).c_str());
+    if(rc)
+        return(rc);
+
+    // This should just be equal to what we put in test 3
+    rc = validate_journal(file_not_found::file_path::str_short,
+                          test_string);
+    if(rc)
+        return(rc);
+
+    rc = validate_journal(file_not_found::file_name::str_short,
+                          "elog_test_4.txt");
+    if(rc)
+        return(rc);
+
+    // Compile fail tests
+
+    // Simple test to prove we fail to compile due to missing param
+    //elog<file_not_found>(file_not_found::errnum(1),
+    //                     file_not_found::file_path("test"));
+
+    // Simple test to prove we fail to compile due to invalid param
+    //elog<file_not_found>(file_not_found::errnum(1),
+    //                     file_not_found::file_path("test"),
+    //                     file_not_found::file_name(1));
+
+    return 0;
+}
+
+
diff --git a/test/Makefile.am b/test/Makefile.am
new file mode 100644
index 0000000..ee0881b
--- /dev/null
+++ b/test/Makefile.am
@@ -0,0 +1,12 @@
+# gtest unit tests which run during a 'make check'
+check_PROGRAMS =
+
+# Run all 'check' test programs
+TESTS = $(check_PROGRAMS)
+
+# Basic test suite for elog interfaces
+check_PROGRAMS += elog_unittest
+elog_unittest_CPPFLAGS = -Igtest $(GTEST_CPPFLAGS)
+elog_unittest_CXXFLAGS = $(PTHREAD_CFLAGS)
+elog_unittest_LDFLAGS = -lgtest_main -lgtest $(PTHREAD_LIBS) $(OESDK_TESTCASE_FLAGS)
+elog_unittest_SOURCES = elog_unittest.cpp
\ No newline at end of file
diff --git a/test/elog_unittest.cpp b/test/elog_unittest.cpp
new file mode 100644
index 0000000..cc14189
--- /dev/null
+++ b/test/elog_unittest.cpp
@@ -0,0 +1,7 @@
+#include <elog.hpp>
+#include <gtest/gtest.h>
+
+// TODO - need to get gtest working in the SDK
+TEST(BasicLog, Zero) {
+    EXPECT_EQ(1, 1);
+}
diff --git a/test/logging_test.cpp b/test/logging_test.cpp
deleted file mode 100644
index 2b75854..0000000
--- a/test/logging_test.cpp
+++ /dev/null
@@ -1,46 +0,0 @@
-#include <iostream>
-#include "elog.hpp"
-#include "log.hpp"
-
-using namespace phosphor;
-using namespace logging;
-
-int main()
-{
-    std::cout << "hello world!\n";
-
-    // Simple elog test
-    const char *test_string = "/tmp/test_string/";
-    elog<file_not_found>(file_not_found::errnum(1),
-                         file_not_found::file_path(test_string),
-                         file_not_found::file_name("elog_test_1.txt"));
-
-    log<level::DEBUG>("Info trace to log file path",
-                      entry(file_not_found::file_path::str,
-                            "/tmp/log_file_test/"));
-
-    // pass parameter and previous_entry
-    elog<file_not_found>(file_not_found::errnum(2),
-                         prev_entry<file_not_found::file_path>(),
-                         file_not_found::file_name("elog_test_2.txt"));
-
-    // Simple test to prove we fail to compile due to missing param
-    //elog<file_not_found>(file_not_found::errnum(1),
-    //                     file_not_found::file_path("test"));
-
-    // Simple test to prove we fail to compile due to invalid param
-    //elog<file_not_found>(file_not_found::errnum(1),
-    //                     file_not_found::file_path("test"),
-    //                     file_not_found::file_name(1));
-
-    // Log tests
-    log<level::DEBUG>("Simple Example");
-
-    const char *file_name = "HELLO.txt";
-    int number = 0xFEFE;
-
-    log<level::DEBUG>("THIS IS A PHOSPHOR LOGGING TEST",
-            entry("FILE_NAME=%s_%x", file_name, number));
-
-    return 0;
-}