summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorVishwanatha Subbanna <vishwa@linux.vnet.ibm.com>2017-09-15 18:50:43 +0530
committerVishwanatha Subbanna <vishwa@linux.vnet.ibm.com>2017-10-12 23:38:38 +0530
commit035a96983cdf8a11a1c2380106c11c94cb8418b2 (patch)
tree18a4360d03f365050f98ff37cab2ca70d946adea
parent36218e6c390401748fb13cd05670aeb86cb8d0c2 (diff)
downloadphosphor-user-manager-035a96983cdf8a11a1c2380106c11c94cb8418b2.tar.gz
phosphor-user-manager-035a96983cdf8a11a1c2380106c11c94cb8418b2.zip
Add GTEST cases
Fixes openbmc/openbmc#1714 Change-Id: I51964f16fc2ea733ee3b3ae822f72ac7b431189a Signed-off-by: Vishwanatha Subbanna <vishwa@linux.vnet.ibm.com>
-rw-r--r--Makefile.am1
-rw-r--r--configure.ac25
-rw-r--r--test/Makefile.am27
-rw-r--r--test/utest.cpp210
-rw-r--r--user.hpp4
5 files changed, 266 insertions, 1 deletions
diff --git a/Makefile.am b/Makefile.am
index 1e1db3e..5d5e14f 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -15,3 +15,4 @@ phosphor_user_manager_LDFLAGS = $(SDBUSPLUS_LIBS) \
phosphor_user_manager_CXXFLAGS = $(SYSTEMD_CFLAGS) \
$(PHOSPHOR_DBUS_INTERFACES_CFLAGS) \
$(PHOSPHOR_LOGGING_CFLAGS)
+SUBDIRS = . test
diff --git a/configure.ac b/configure.ac
index c6bb3fe..6d41a5d 100644
--- a/configure.ac
+++ b/configure.ac
@@ -26,6 +26,29 @@ AC_ARG_VAR(DEFAULT_CRYPT_ALGO, [The default crypt algorithm if one not found in
AS_IF([test "x$DEFAULT_CRYPT_ALGO" == "x"], [DEFAULT_CRYPT_ALGO="1"])
AC_DEFINE_UNQUOTED([DEFAULT_CRYPT_ALGO], ["$DEFAULT_CRYPT_ALGO"], [The default crypt algorithm if one not found in shadow])
+# 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])
+)
+
# Checks for typedefs, structures, and compiler characteristics.
AX_CXX_COMPILE_STDCXX_14([noext])
AX_APPEND_COMPILE_FLAGS([-Wall -Werror], [CXXFLAGS])
@@ -34,5 +57,5 @@ AX_APPEND_COMPILE_FLAGS([-Wall -Werror], [CXXFLAGS])
LT_INIT
# 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..3acdaab
--- /dev/null
+++ b/test/Makefile.am
@@ -0,0 +1,27 @@
+AM_CPPFLAGS = -I$(top_srcdir)
+
+# Run all 'check' test programs
+TESTS = $(check_PROGRAMS)
+
+# Build/add utest to test suite
+check_PROGRAMS = utest
+utest_CPPFLAGS = -Igtest \
+ $(GTEST_CPPFLAGS) \
+ $(AM_CPPFLAGS) \
+ $(PHOSPHOR_LOGGING_CFLAGS) \
+ $(SDBUSPLUS_CFLAGS)
+
+utest_CXXFLAGS = $(PTHREAD_CFLAGS)
+
+utest_LDFLAGS = -lgtest_main \
+ -lgtest \
+ $(PTHREAD_LIBS) \
+ $(OESDK_TESTCASE_FLAGS) \
+ $(PHOSPHOR_DBUS_INTERFACES_LIBS) \
+ $(PHOSPHOR_LOGGING_LIBS) \
+ $(SDBUSPLUS_LIBS) \
+ -lcrypt \
+ -lstdc++fs
+
+utest_SOURCES = utest.cpp
+utest_LDADD = $(top_builddir)/user.o
diff --git a/test/utest.cpp b/test/utest.cpp
new file mode 100644
index 0000000..bdca968
--- /dev/null
+++ b/test/utest.cpp
@@ -0,0 +1,210 @@
+#include <sys/stat.h>
+#include <string>
+#include <fstream>
+#include <experimental/filesystem>
+#include <gtest/gtest.h>
+#include <sdbusplus/bus.hpp>
+#include "user.hpp"
+namespace phosphor
+{
+namespace user
+{
+
+namespace fs = std::experimental::filesystem;
+
+constexpr auto path = "/dummy/user";
+constexpr auto testShadow = "/tmp/__tshadow__";
+constexpr auto shadowCopy = "/tmp/__tshadowCopy__";
+constexpr auto shadowCompare = "/tmp/__tshadowCompare__";
+
+// New password
+constexpr auto password = "passw0rd";
+
+constexpr auto MD5 = "1";
+constexpr auto SHA512 = "6";
+constexpr auto salt = "1G.cK/YP";
+
+// Example entry matching /etc/shadow structure
+constexpr auto spPwdp = "$1$1G.cK/YP$JI5t0oliPxZveXOvLcZ/H.:17344:1:90:7:::";
+
+class UserTest : public ::testing::Test
+{
+ public:
+ const std::string md5Salt = '$' + std::string(MD5) + '$'
+ + std::string(salt) + '$';
+ const std::string shaSalt = '$' + std::string(SHA512) + '$'
+ + std::string(salt) + '$';
+
+ const std::string entry = fs::path(path).filename().string() +
+ ':' + std::string(spPwdp);
+ sdbusplus::bus::bus bus;
+ phosphor::user::User user;
+
+ // Gets called as part of each TEST_F construction
+ UserTest()
+ : bus(sdbusplus::bus::new_default()),
+ user(bus, path)
+ {
+ // Create a shadow file entry
+ std::ofstream file(testShadow);
+ file << entry;
+ file.close();
+
+ // File to compare against
+ std::ofstream compare(shadowCompare);
+ compare << entry;
+ compare.close();
+ }
+
+ // Gets called as part of each TEST_F destruction
+ ~UserTest()
+ {
+ if (fs::exists(testShadow))
+ {
+ fs::remove(testShadow);
+ }
+
+ if (fs::exists(shadowCopy))
+ {
+ fs::remove(shadowCopy);
+ }
+
+ if (fs::exists(shadowCompare))
+ {
+ fs::remove(shadowCompare);
+ }
+ }
+
+ /** @brief wrapper for get crypt field */
+ auto getCryptField(char* data)
+ {
+ return User::getCryptField(
+ std::forward<decltype(data)>(data));
+ }
+
+ /** @brief wrapper for getSaltString */
+ auto getSaltString(const std::string& crypt,
+ const std::string& salt)
+ {
+ return User::getSaltString(
+ std::forward<decltype(crypt)>(crypt),
+ std::forward<decltype(salt)>(salt));
+ }
+
+ /** @brief wrapper for generateHash */
+ auto generateHash(const std::string& password,
+ const std::string& salt)
+ {
+ return User::generateHash(
+ std::forward<decltype(password)>(password),
+ std::forward<decltype(salt)>(salt));
+ }
+
+ /** @brief Applies the new password */
+ auto applyPassword()
+ {
+ return user.applyPassword(testShadow, shadowCopy,
+ password, salt);
+ }
+};
+
+/** @brief Makes sure that SHA512 crypt field is extracted
+ */
+TEST_F(UserTest, sha512GetCryptField)
+{
+ auto salt = const_cast<char*>(shaSalt.c_str());
+ EXPECT_EQ(SHA512, this->getCryptField(salt));
+}
+
+/** @brief Makes sure that MD5 crypt field is extracted as default
+ */
+TEST_F(UserTest, md55GetCryptFieldDefault)
+{
+ auto salt = const_cast<char*>("hello");
+ EXPECT_EQ(MD5, this->getCryptField(salt));
+}
+
+/** @brief Makes sure that MD5 crypt field is extracted
+ */
+TEST_F(UserTest, md55GetCryptField)
+{
+ auto salt = const_cast<char*>(md5Salt.c_str());
+ EXPECT_EQ(MD5, this->getCryptField(salt));
+}
+
+/** @brief Makes sure that salt string is put within $$
+ */
+TEST_F(UserTest, getSaltString)
+{
+ EXPECT_EQ(md5Salt, this->getSaltString(MD5, salt));
+}
+
+/** @brief Makes sure hash is generated correctly
+ */
+TEST_F(UserTest, generateHash)
+{
+ std::string sample = crypt(password, md5Salt.c_str());
+ std::string actual = generateHash(password, md5Salt);
+ EXPECT_EQ(sample, actual);
+}
+
+/** @brief Verifies that the correct password is written to file
+ */
+TEST_F(UserTest, applyPassword)
+{
+ // Update the password
+ applyPassword();
+
+ // Read files and compare
+ std::ifstream shadow(testShadow);
+ std::ifstream copy(shadowCompare);
+
+ std::string shadowEntry;
+ shadow >> shadowEntry;
+
+ std::string shadowCompareEntry;
+ copy >> shadowCompareEntry;
+
+ EXPECT_EQ(shadowEntry, shadowCompareEntry);
+}
+
+/** @brief Verifies the shadow copy file is removed
+ */
+TEST_F(UserTest, checkShadowCopyRemove)
+{
+ // Update the password so that the temp file is in action
+ applyPassword();
+
+ // Compare the permission of 2 files
+ struct stat shadow{};
+ struct stat temp{};
+
+ stat(testShadow, &shadow);
+ stat(shadowCopy, &temp);
+ EXPECT_EQ(false, fs::exists(shadowCopy));
+}
+
+/** @brief Verifies the permissions are correct
+ */
+TEST_F(UserTest, verifyShadowPermission)
+{
+ // Change the permission to 400-> -r--------
+ chmod(testShadow, S_IRUSR);
+ chmod(shadowCompare, S_IRUSR);
+
+ // Update the password so that the temp file is in action
+ applyPassword();
+
+ // Compare the permission of 2 files.
+ // file rename would make sure that the permissions
+ // of old are moved to new
+ struct stat shadow{};
+ struct stat compare{};
+
+ stat(testShadow, &shadow);
+ stat(shadowCompare, &compare);
+ EXPECT_EQ(shadow.st_mode, compare.st_mode);
+}
+
+} // namespace user
+} // namespace phosphor
diff --git a/user.hpp b/user.hpp
index 44dd3a3..c05cacc 100644
--- a/user.hpp
+++ b/user.hpp
@@ -55,6 +55,7 @@ class User : public Interface
*/
void setPassword(std::string newPassword) override;
+
private:
/** @brief sdbusplus handler */
sdbusplus::bus::bus& bus;
@@ -128,6 +129,9 @@ class User : public Interface
*/
void raiseException(int errNo,
const std::string& errMsg);
+
+ /** @brief For enabling test cases */
+ friend class UserTest;
};
} // namespace user
OpenPOWER on IntegriCloud