unit-test: Introduce unit tests to phosphor-objmgr

Move a function to make more testable and add a test case for it

Testing: Verified 100% test coverage in processing.cpp

Change-Id: I0a888009cfeb57bbc8ad295681bea00b79f2a23d
Signed-off-by: Andrew Geissler <geissonator@yahoo.com>
diff --git a/.gitignore b/.gitignore
index e4b4a64..8ba237b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -91,6 +91,7 @@
 Makefile
 Makefile.in
 aclocal.m4
+aminclude_static.am
 ar-lib
 *-libtool
 autom4te.cache*
diff --git a/Makefile.am b/Makefile.am
index caa13dc..62fe99e 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1,3 +1,18 @@
+if AUTOCONF_CODE_COVERAGE_2019_01_06
+include $(top_srcdir)/aminclude_static.am
+clean-local: code-coverage-clean
+distclean-local: code-coverage-dist-clean
+else
+@CODE_COVERAGE_RULES@
+endif
+
+AM_LIBS = $(CODE_COVERAGE_LIBS)
+AM_CPPFLAGS = $(CODE_COVERAGE_CPPFLAGS) -UNDEBUG $(GTEST_CPPFLAGS)
+AM_CFLAGS = $(CODE_COVERAGE_CFLAGS)
+AM_CXXFLAGS = $(CODE_COVERAGE_CXXFLAGS)
+AM_LDFLAGS = $(GMOCK_LIBS) -lgmock_main \
+	$(GTEST_LIBS) $(OESDK_TESTCASE_FLAGS) $(PTHREAD_LIBS)
+
 sbin_PROGRAMS = mapper mapperx
 
 mapper_SOURCES = libmapper/app.c
@@ -11,7 +26,7 @@
 libmapper_la_LDFLAGS = $(SYSTEMD_LIBS) -version-info 1:0:0 -shared
 libmapper_la_CFLAGS = $(SYSTEMD_CFLAGS)
 
-mapperx_SOURCES = src/main.cpp src/argument.cpp
+mapperx_SOURCES = src/main.cpp src/argument.cpp src/processing.cpp
 mapperx_LDFLAGS = $(SDBUSPLUS_LIBS) -pthread -ltinyxml2
 mapperx_CXXFLAGS = $(SYSTEMD_CFLAGS) -DBOOST_SYSTEM_NO_DEPRECATED -DBOOST_ERROR_CODE_HEADER_ONLY -DBOOST_ALL_NO_LIB
 
@@ -40,3 +55,13 @@
 endif
 
 SUBDIRS = fail-monitor
+
+check_PROGRAMS =
+XFAIL_TESTS =
+
+# Ignore system headers
+CODE_COVERAGE_IGNORE_PATTERN = '/include/*' '/usr/include/*' '$(includedir)/*'
+
+include src/test/Makefile.am.include
+
+TESTS = $(check_PROGRAMS)
diff --git a/configure.ac b/configure.ac
index 69cc916..030e72a 100644
--- a/configure.ac
+++ b/configure.ac
@@ -3,7 +3,7 @@
 AC_INIT([phosphor-objmgr], [1.0], [https://github.com/openbmc/phosphor-objmgr/issues])
 AC_CONFIG_HEADERS([libmapper/config.h])
 AC_CONFIG_MACRO_DIR([m4])
-AM_INIT_AUTOMAKE([subdir-objects -Wall -Werror foreign dist-xz])
+AM_INIT_AUTOMAKE([subdir-objects -Wall -Wno-portability -Werror foreign dist-xz])
 AM_SILENT_RULES([yes])
 AC_LANG([C++])
 
@@ -67,6 +67,89 @@
     AC_SUBST([OESDK_TESTCASE_FLAGS], [$testcase_flags])
 )
 
+# Make it possible for users to choose if they want test support
+# explicitly or not at all
+AC_ARG_ENABLE([tests], AC_HELP_STRING([--disable-tests],
+                                      [Build test cases]))
+
+ # Check/set gtest specific functions
+AS_IF([test "x$enable_tests" != "xno"], [
+    PKG_CHECK_MODULES([GTEST], [gtest], [], [true])
+    PKG_CHECK_MODULES([GMOCK], [gmock], [], [true])
+    AX_PTHREAD
+
+    AX_SAVE_FLAGS_WITH_PREFIX(OLD, [CPPFLAGS])
+    AX_APPEND_COMPILE_FLAGS([$GTEST_CFLAGS], [CPPFLAGS])
+    AC_CHECK_HEADERS([gtest/gtest.h], [
+        AS_IF([test "x$GTEST_CFLAGS" = "x"], [
+            AS_IF([test "x$PTHREAD_CFLAGS" = "x"], [
+                AX_APPEND_COMPILE_FLAGS(["-DGTEST_HAS_PTHREAD=0"], [GTEST_CFLAGS])
+            ], [
+                AX_APPEND_COMPILE_FLAGS(["-DGTEST_HAS_PTHREAD=1"], [GTEST_CFLAGS])
+                AX_APPEND_COMPILE_FLAGS([$PTHREAD_CFLAGS], [GTEST_CFLAGS])
+            ])
+        ])
+    ], [
+        AS_IF([test "x$enable_tests" = "xyes"], [
+            AC_MSG_ERROR([Testing enabled but could not find gtest/gtest.h])
+        ])
+    ])
+    AX_RESTORE_FLAGS_WITH_PREFIX(OLD, [CPPFLAGS])
+
+    AX_SAVE_FLAGS_WITH_PREFIX(OLD, [CPPFLAGS])
+    AX_APPEND_COMPILE_FLAGS([$GMOCK_CFLAGS], [CPPFLAGS])
+    AC_CHECK_HEADERS([gmock/gmock.h], [], [
+        AS_IF([test "x$enable_tests" = "xyes"], [
+            AC_MSG_ERROR([Testing enabled but could not find gmock/gmock.h])
+        ])
+    ])
+    AX_RESTORE_FLAGS_WITH_PREFIX(OLD, [CPPFLAGS])
+
+    AX_SAVE_FLAGS_WITH_PREFIX(OLD, [LDFLAGS])
+    AX_APPEND_COMPILE_FLAGS([$GTEST_LIBS], [LDFLAGS])
+    AC_CHECK_LIB([gtest], [main], [
+        AS_IF([test "x$GTEST_LIBS" = "x"], [
+            AX_APPEND_COMPILE_FLAGS([-lgtest], [GTEST_LIBS])
+        ])
+    ], [
+        AS_IF([test "x$enable_tests" = "xyes"], [
+            AC_MSG_ERROR([Testing enabled but couldn't find gtest libs])
+        ])
+    ])
+    AX_RESTORE_FLAGS_WITH_PREFIX(OLD, [LDFLAGS])
+
+    AX_SAVE_FLAGS_WITH_PREFIX(OLD, [LDFLAGS])
+    AX_APPEND_COMPILE_FLAGS([$GMOCK_LIBS], [LDFLAGS])
+    AC_CHECK_LIB([gmock], [main], [
+        AS_IF([test "x$GMOCK_LIBS" = "x"], [
+            AX_APPEND_COMPILE_FLAGS([-lgmock], [GMOCK_LIBS])
+        ])
+    ], [
+        AS_IF([test "x$enable_tests" = "xyes"], [
+            AC_MSG_ERROR([Testing enabled but couldn't find gmock libs])
+        ])
+    ])
+    AX_RESTORE_FLAGS_WITH_PREFIX(OLD, [LDFLAGS])
+])
+
+# Check for valgrind
+AS_IF([test "x$enable_tests" = "xno"], [enable_valgrind=no])
+m4_foreach([vgtool], [valgrind_tool_list],
+    [AX_VALGRIND_DFLT(vgtool, [off])])
+AX_VALGRIND_DFLT([memcheck], [on])
+AX_VALGRIND_CHECK
+AM_EXTRA_RECURSIVE_TARGETS([check-valgrind])
+m4_foreach([vgtool], [valgrind_tool_list],
+    [AM_EXTRA_RECURSIVE_TARGETS([check-valgrind-]vgtool)])
+
+# Code coverage
+AX_CODE_COVERAGE
+
+m4_ifdef([_AX_CODE_COVERAGE_RULES],
+    [AM_CONDITIONAL(AUTOCONF_CODE_COVERAGE_2019_01_06, [true])],
+    [AM_CONDITIONAL(AUTOCONF_CODE_COVERAGE_2019_01_06, [false])])
+AX_ADD_AM_MACRO_STATIC([])
+
 # Add defines to config header
 AC_DEFINE([MAPPER_BUSNAME], ["xyz.openbmc_project.ObjectMapper"], [Object mapper bus name])
 AC_DEFINE([MAPPER_INTERFACE], ["xyz.openbmc_project.ObjectMapper"], [Object mapper interface])
diff --git a/src/main.cpp b/src/main.cpp
index 7df7aa3..5870aea 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -1,3 +1,4 @@
+#include "processing.hpp"
 #include "src/argument.hpp"
 
 #include <tinyxml2.h>
@@ -85,26 +86,6 @@
     };
 };
 
-bool get_well_known(
-    boost::container::flat_map<std::string, std::string>& owners,
-    const std::string& request, std::string& well_known)
-{
-    // If it's already a well known name, just return
-    if (!boost::starts_with(request, ":"))
-    {
-        well_known = request;
-        return true;
-    }
-
-    auto it = owners.find(request);
-    if (it == owners.end())
-    {
-        return false;
-    }
-    well_known = it->second;
-    return true;
-}
-
 void update_owners(sdbusplus::asio::connection* conn,
                    boost::container::flat_map<std::string, std::string>& owners,
                    const std::string& new_object)
@@ -882,7 +863,7 @@
                 interfaces_added;
             message.read(obj_path, interfaces_added);
             std::string well_known;
-            if (!get_well_known(name_owners, message.get_sender(), well_known))
+            if (!getWellKnown(name_owners, message.get_sender(), well_known))
             {
                 return; // only introspect well-known
             }
@@ -986,7 +967,7 @@
             }
 
             std::string sender;
-            if (!get_well_known(name_owners, message.get_sender(), sender))
+            if (!getWellKnown(name_owners, message.get_sender(), sender))
             {
                 return;
             }
@@ -1043,8 +1024,8 @@
                             std::vector<Association>>(findAssociations->second);
 
                     std::string well_known;
-                    if (!get_well_known(name_owners, message.get_sender(),
-                                        well_known))
+                    if (!getWellKnown(name_owners, message.get_sender(),
+                                      well_known))
                     {
                         return;
                     }
diff --git a/src/processing.cpp b/src/processing.cpp
new file mode 100644
index 0000000..923e147
--- /dev/null
+++ b/src/processing.cpp
@@ -0,0 +1,23 @@
+#include "processing.hpp"
+
+#include <boost/algorithm/string/predicate.hpp>
+
+bool getWellKnown(
+    const boost::container::flat_map<std::string, std::string>& owners,
+    const std::string& request, std::string& wellKnown)
+{
+    // If it's already a well known name, just return
+    if (!boost::starts_with(request, ":"))
+    {
+        wellKnown = request;
+        return true;
+    }
+
+    auto it = owners.find(request);
+    if (it == owners.end())
+    {
+        return false;
+    }
+    wellKnown = it->second;
+    return true;
+}
diff --git a/src/processing.hpp b/src/processing.hpp
new file mode 100644
index 0000000..27ef4c6
--- /dev/null
+++ b/src/processing.hpp
@@ -0,0 +1,18 @@
+#pragma once
+
+#include <boost/container/flat_map.hpp>
+#include <string>
+
+/** @brief Get well known name of input unique name
+ *
+ * If user passes in well known name then that will be returned.
+ *
+ * @param[in] owners       - Current list of owners
+ * @param[in] request      - The name to look up
+ * @param[out] wellKnown   - The well known name if found
+ *
+ * @return True if well known name is found, false otherwise
+ */
+bool getWellKnown(
+    const boost::container::flat_map<std::string, std::string>& owners,
+    const std::string& request, std::string& wellKnown);
diff --git a/src/test/Makefile.am.include b/src/test/Makefile.am.include
new file mode 100644
index 0000000..a698753
--- /dev/null
+++ b/src/test/Makefile.am.include
@@ -0,0 +1,4 @@
+src_test_well_known_SOURCES = %reldir%/well_known.cpp src/processing.cpp
+
+check_PROGRAMS += \
+	%reldir%/well_known
diff --git a/src/test/well_known.cpp b/src/test/well_known.cpp
new file mode 100644
index 0000000..c9f119c
--- /dev/null
+++ b/src/test/well_known.cpp
@@ -0,0 +1,37 @@
+#include "src/processing.hpp"
+
+#include <gtest/gtest.h>
+
+// Verify if name does not start with a : that it is returned
+TEST(WellKnownName, NameNotStartColon)
+{
+    boost::container::flat_map<std::string, std::string> owners;
+    const std::string request = "test";
+    std::string well_known;
+
+    EXPECT_TRUE(getWellKnown(owners, request, well_known));
+    EXPECT_EQ(well_known, request);
+}
+
+// Verify if name is not found, false is returned
+TEST(WellKnownName, NameNotFound)
+{
+    boost::container::flat_map<std::string, std::string> owners;
+    const std::string request = ":test";
+    std::string well_known;
+
+    EXPECT_FALSE(getWellKnown(owners, request, well_known));
+    EXPECT_TRUE(well_known.empty());
+}
+
+// Verify if name is found, true is returned and name is correct
+TEST(WellKnownName, NameFound)
+{
+    boost::container::flat_map<std::string, std::string> owners;
+    const std::string request = ":1.25";
+    std::string well_known;
+
+    owners[request] = "test";
+    EXPECT_TRUE(getWellKnown(owners, request, well_known));
+    EXPECT_EQ(well_known, "test");
+}