Add unit test for certificate install

Change-Id: I25700842983be152edc18f78912a917220867b0b
Signed-off-by: Marri Devender Rao <devenrao@in.ibm.com>
diff --git a/Makefile.am b/Makefile.am
index a597c38..7ff740a 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -21,3 +21,5 @@
 phosphor_certificate_manager_CXXFLAGS = \
 	$(SYSTEMD_CFLAGS) \
 	$(PHOSPHOR_DBUS_INTERFACES_CFLAGS)
+
+SUBDIRS = . test
diff --git a/certs_manager.hpp b/certs_manager.hpp
index a23f044..f001df7 100644
--- a/certs_manager.hpp
+++ b/certs_manager.hpp
@@ -68,10 +68,10 @@
 
   private:
     /** @brief Client certificate Installation helper function **/
-    void clientInstall();
+    virtual void clientInstall();
 
     /** @brief Server certificate Installation helper function **/
-    void serverInstall();
+    virtual void serverInstall();
 
     /** @brief systemd unit reload helper function
      * @param[in] unit - service need to reload.
diff --git a/configure.ac b/configure.ac
index 02a0a94..da35058 100644
--- a/configure.ac
+++ b/configure.ac
@@ -29,6 +29,27 @@
 # Code coverage
 AX_CODE_COVERAGE
 
+# Check/set gtest specific functions.
+AX_PTHREAD([GTEST_CPPFLAGS="-DGTEST_HAS_PTHREAD=1"],[GTEST_CPPFLAGS="-DGTEST_HAS_PTHREAD=0"])
+AC_SUBST(GTEST_CPPFLAGS)
+
+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_ARG_VAR(BUSNAME, [The D-Bus busname to own])
 AS_IF([test "x$BUSNAME" == "x"], [BUSNAME="xyz.openbmc_project.Certs.Manager"])
 AC_DEFINE_UNQUOTED([BUSNAME], ["$BUSNAME"], [The D-Bus busname to own])
@@ -37,5 +58,5 @@
 AC_DEFINE_UNQUOTED([OBJPATH], ["$OBJPATH"], [The certificate manager D-Bus root])
 
 # Create configured output
-AC_CONFIG_FILES([Makefile])
+AC_CONFIG_FILES([Makefile test/Makefile])
 AC_OUTPUT
diff --git a/test/Makefile.am b/test/Makefile.am
new file mode 100644
index 0000000..814966e
--- /dev/null
+++ b/test/Makefile.am
@@ -0,0 +1,33 @@
+AM_CPPFLAGS = \
+    -I$(top_srcdir) \
+    $(PTHREAD_CPPFLAGS) \
+    $(SDBUSPLUS_CPPFLAGS) \
+    $(PHOSPHOR_DBUS_INTERFACES_CPPFLAGS)
+
+AM_CFLAGS = \
+    $(PTHREAD_CFLAGS) \
+    $(SDBUSPLUS_CFLAGS) \
+    $(PHOSPHOR_DBUS_INTERFACES_CFLAGS)
+
+AM_CXXFLAGS = \
+    $(PTHREAD_CXXFLAGS) \
+    $(SDBUSPLUS_CXXFLAGS) \
+    $(PHOSPHOR_DBUS_INTERFACES_CXXFLAGS)
+
+AM_LDFLAGS = \
+    -lstdc++fs \
+    $(SDBUSPLUS_LIBS) \
+    $(PHOSPHOR_DBUS_INTERFACES_LIBS) \
+    -lgtest -lgtest_main -lgmock $(PTHREAD_CFLAGS) $(OESDK_TESTCASE_FLAGS)
+
+check_PROGRAMS =
+
+# Run all 'check' test programs
+TESTS = $(check_PROGRAMS)
+
+# Build/add certs_manager_test to test suite
+check_PROGRAMS += certs_manager_test
+certs_manager_test_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_CPPFLAGS) 
+certs_manager_test_LDFLAGS = $(AM_LDFLAGS) $(PTHREAD_LIBS) $(OESDK_TESTCASE_FLAGS)
+certs_manager_test_SOURCES = certs_manager_test.cpp
+certs_manager_test_LDADD = $(top_builddir)/certs_manager.o
diff --git a/test/certs_manager_test.cpp b/test/certs_manager_test.cpp
new file mode 100644
index 0000000..aaf491c
--- /dev/null
+++ b/test/certs_manager_test.cpp
@@ -0,0 +1,168 @@
+#include "certs_manager.hpp"
+
+#include <algorithm>
+#include <experimental/filesystem>
+#include <fstream>
+#include <iterator>
+#include <string>
+#include <xyz/openbmc_project/Certs/Install/error.hpp>
+#include <xyz/openbmc_project/Common/error.hpp>
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+namespace fs = std::experimental::filesystem;
+static constexpr auto BUSNAME = "xyz.openbmc_project.Certs.Manager";
+static constexpr auto OBJPATH = "/xyz/openbmc_project/certs";
+using InternalFailure =
+    sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
+
+class TestCertsManager : public ::testing::Test
+{
+  public:
+    TestCertsManager() : bus(sdbusplus::bus::new_default())
+    {
+    }
+    void SetUp() override
+    {
+        char dirTemplate[] = "/tmp/FakeCerts.XXXXXX";
+        auto dirPtr = mkdtemp(dirTemplate);
+        if (dirPtr == NULL)
+        {
+            throw std::bad_alloc();
+        }
+        certDir = dirPtr;
+        certificateFile = "cert.pem";
+        std::string cmd = "openssl req -x509 -sha256 -newkey rsa:2048 ";
+        cmd += "-keyout cert.pem -out cert.pem -days 3650 ";
+        cmd += "-subj "
+               "/O=openbmc-project.xyz/CN=localhost"
+               " -nodes";
+        auto val = std::system(cmd.c_str());
+        if (val)
+        {
+            std::cout << "COMMAND Error: " << val << std::endl;
+        }
+    }
+    void TearDown() override
+    {
+        fs::remove_all(certDir);
+        fs::remove(certificateFile);
+    }
+
+    bool compareFiles(const std::string& file1, const std::string& file2)
+    {
+        std::ifstream f1(file1, std::ifstream::binary | std::ifstream::ate);
+        std::ifstream f2(file2, std::ifstream::binary | std::ifstream::ate);
+
+        if (f1.fail() || f2.fail())
+        {
+            return false; // file problem
+        }
+
+        if (f1.tellg() != f2.tellg())
+        {
+            return false; // size mismatch
+        }
+
+        // seek back to beginning and use std::equal to compare contents
+        f1.seekg(0, std::ifstream::beg);
+        f2.seekg(0, std::ifstream::beg);
+        return std::equal(std::istreambuf_iterator<char>(f1.rdbuf()),
+                          std::istreambuf_iterator<char>(),
+                          std::istreambuf_iterator<char>(f2.rdbuf()));
+    }
+
+  protected:
+    sdbusplus::bus::bus bus;
+    std::string certificateFile;
+
+    std::string certDir;
+};
+
+class MainApp
+{
+  public:
+    MainApp(phosphor::certs::Manager* manager) : manager(manager)
+    {
+    }
+    void install(std::string& path)
+    {
+        manager->install(path);
+    }
+    phosphor::certs::Manager* manager;
+};
+
+class MockCertManager : public phosphor::certs::Manager
+{
+  public:
+    MockCertManager(sdbusplus::bus::bus& bus, const char* path,
+                    std::string& type, std::string&& unit,
+                    std::string&& certPath) :
+        Manager(bus, path, type, std::forward<std::string>(unit),
+                std::forward<std::string>(certPath))
+    {
+    }
+    virtual ~MockCertManager()
+    {
+    }
+
+    MOCK_METHOD0(clientInstall, void());
+    MOCK_METHOD0(serverInstall, void());
+};
+
+/** @brief Check if server install routine is invoked for server setup
+ */
+TEST_F(TestCertsManager, InvokeServerInstall)
+{
+    std::string endpoint("https");
+    std::string unit("nginx.service");
+    std::string type("server");
+    std::string path(certDir + "/" + certificateFile);
+    std::string verifyPath(path);
+    auto objPath = std::string(OBJPATH) + '/' + type + '/' + endpoint;
+    MockCertManager manager(bus, objPath.c_str(), type, std::move(unit),
+                            std::move(path));
+    EXPECT_CALL(manager, serverInstall()).Times(1);
+
+    MainApp mainApp(&manager);
+    EXPECT_NO_THROW({ mainApp.install(certificateFile); });
+    EXPECT_TRUE(fs::exists(verifyPath));
+}
+
+/** @brief Check if client install routine is invoked for client setup
+ */
+TEST_F(TestCertsManager, InvokeClientInstall)
+{
+    std::string endpoint("ldap");
+    std::string unit("nslcd.service");
+    std::string type("client");
+    std::string path(certDir + "/" + certificateFile);
+    std::string verifyPath(path);
+    auto objPath = std::string(OBJPATH) + '/' + type + '/' + endpoint;
+    MockCertManager manager(bus, objPath.c_str(), type, std::move(unit),
+                            std::move(path));
+    EXPECT_CALL(manager, clientInstall()).Times(1);
+    MainApp mainApp(&manager);
+    EXPECT_NO_THROW({ mainApp.install(certificateFile); });
+    EXPECT_TRUE(fs::exists(verifyPath));
+}
+
+/** @brief Compare the installed certificate with the copied certificate
+ */
+TEST_F(TestCertsManager, CompareInstalledCertificate)
+{
+    std::string endpoint("ldap");
+    std::string unit("nslcd.service");
+    std::string type("client");
+    std::string path(certDir + "/" + certificateFile);
+    std::string verifyPath(path);
+    auto objPath = std::string(OBJPATH) + '/' + type + '/' + endpoint;
+    MockCertManager manager(bus, objPath.c_str(), type, std::move(unit),
+                            std::move(path));
+    EXPECT_CALL(manager, clientInstall()).Times(1);
+    MainApp mainApp(&manager);
+    EXPECT_NO_THROW({ mainApp.install(certificateFile); });
+    EXPECT_TRUE(fs::exists(verifyPath));
+    EXPECT_TRUE(compareFiles(verifyPath, certificateFile));
+}