经验分享: 一个被否的方案中的UT示例, 系列之 2

当前方案是解决container部署下,非root用户下添加user/group功能,比较极端,后来大专家给出了另外的方案,当前极端方案被否,UT方面比较有代表性,可以作为样例,说不定以后用得上


C:.
│  Makefile.am
│
└─userprofileconfig
    ├─include
    │      sysFuncInterface.hpp
    │      sysFuncInterfaceImpl.hpp
    │      userprofileconfig.hpp
    │
    ├─src
    │      main.cpp
    │      userprofileconfig.cpp
    │
    └─tst
            userprofileconfigTest.cpp
            userprofilesystemMock.hpp

userprofileconfig.hpp


#ifndef YOUR_OWN_USERPROFILECONFIG_HPP
#define YOUR_OWN_USERPROFILECONFIG_HPP

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <sysFuncInterface.hpp>

class userProfileConfig
{
    static constexpr auto USRPROFILE_READ_WRITE = "ab+";
    static constexpr auto USRPROFILE_FILENAME_GROUP = "/etc/group";
    static constexpr auto USRPROFILE_FILENAME_PWD = "/etc/passwd";
    static constexpr auto USRPROFILE_USER_NAME = "YOUR_OWN";
    static constexpr auto USRPROFILE_PASSWD_STRING = "%s:x:%d:%d::/home/YOUR_OWN:/bin/bash\n";
    static constexpr auto USRPROFILE_GROUP_STRING = "%s:x:%d:\n";

    static const int USRPROFILECONFIG_OK = 0;
    static const int USRPROFILECONFIG_NOK = 1;

public:
    userProfileConfig(){}
    ~userProfileConfig(){}

    int updateGroupAndUsr(sysFuncInterface* sysFuncImplimentation);
private:
    int addUser(FILE *fp_password, const char* userName);
    int updateUserName(FILE *fp_password);
    int addGroup(FILE *fp_group, const char* userName);
    int updateGroupName(FILE *fp_group);
    sysFuncInterface* sysFuncImpl;
};
#endif //YOUR_OWN_USERPROFILECONFIG_HPP

sysFuncInterface.hpp


#ifndef YOUR_OWN_SYSFUNCINTERFACE_HPP
#define YOUR_OWN_SYSFUNCINTERFACE_HPP

#include<grp.h>
#include<sys/types.h>
#include<unistd.h>
#include <pwd.h>
class sysFuncInterface {
public:
    virtual ~sysFuncInterface() {}

    virtual FILE* FileOpen(const char* filename, const char* mode) = 0;
    virtual size_t FileWrite(const void* data, size_t size, size_t num, FILE* file) = 0;
    virtual int FileClose(FILE* file) = 0;

    virtual uid_t Getuid() = 0;
    virtual uid_t Geteuid() = 0;
    virtual gid_t Getgid() = 0;
    virtual struct group * Getgrgid(gid_t gid) = 0;
    virtual struct passwd *Getpwnam(const char *name) = 0;

};

#endif //YOUR_OWN_SYSFUNCINTERFACE_HPP

sysFuncInterfaceImpl.hpp


#ifndef YOUR_OWN_SYSFUNCINTERFACEIMPL_HPP
#define YOUR_OWN_SYSFUNCINTERFACEIMPL_HPP

#include<sys/types.h>
#include<unistd.h>
#include <sysFuncInterface.hpp>

class sysFuncInterfaceImpl : public sysFuncInterface
{
public:
    virtual FILE* FileOpen(const char* filename, const char* mode)
    {
        return fopen(filename, mode);
    }

    virtual size_t FileWrite(const void* data, size_t size, size_t num, FILE* file)
    {
        return fwrite(data, size, num, file);
    }

    virtual int FileClose(FILE* file)
    {
        return fclose(file);
    }
    virtual uid_t Getuid(void)
    {
        return getuid();
    }
    virtual uid_t Geteuid(void)
    {
        return geteuid();
    }
    virtual gid_t Getgid(void)
    {
        return getgid();
    }
    virtual struct group * Getgrgid(gid_t gid)
    {
        return getgrgid(gid);
    }
    virtual struct passwd *Getpwnam(const char *name)
    {
        return getpwnam(name);
    }
};

#endif //YOUR_OWN_SYSFUNCINTERFACEIMPL_HPP

userprofileconfig.cpp

#include <cstring>
#include <cerrno>
#include <common/include/log.h>
#include <userprofileconfig.hpp>

int userProfileConfig::addUser(FILE *fp_password, const char* userName)
{
    int ret = USRPROFILECONFIG_OK;
    char buffer[256] = {0};
    snprintf(buffer, sizeof(buffer), USRPROFILE_PASSWD_STRING, userName, sysFuncImpl->Getuid(), sysFuncImpl->Getgid() );

    if(sysFuncImpl->FileWrite(buffer, sizeof(char), strlen(buffer), fp_password) != (strlen(buffer)))
    {
        TRS_ERR("failed to write, line %d, len %ld, error[%s].\n", __LINE__, strlen(buffer), strerror(errno));
        ret = USRPROFILECONFIG_NOK;
    }
    return ret;
}

int userProfileConfig::updateUserName(FILE *fp_password)
{
    struct passwd *pwdInfo = NULL;
    pwdInfo = sysFuncImpl->Getpwnam(USRPROFILE_USER_NAME);
    if(NULL == pwdInfo)
    {
        return addUser(fp_password, USRPROFILE_USER_NAME);
    }
    return USRPROFILECONFIG_OK;
}

int userProfileConfig::addGroup(FILE *fp_group, const char* userName)
{
    int ret = USRPROFILECONFIG_OK;
    char buffer[256] = {0};
    snprintf(buffer, sizeof(buffer), USRPROFILE_GROUP_STRING, userName, sysFuncImpl->Getgid() );

    if(sysFuncImpl->FileWrite(buffer, sizeof(char), strlen(buffer), fp_group) != (strlen(buffer)))
    {
        TRS_ERR("failed to write, line %d, len %ld, error[%s].\n", __LINE__, strlen(buffer), strerror(errno));
        ret = USRPROFILECONFIG_NOK;
    }
    return ret;
}

int userProfileConfig::updateGroupName(FILE *fp_group)
{
    struct group *grp = sysFuncImpl->Getgrgid(sysFuncImpl->Getgid());
    if (NULL == grp)
    {
        return addGroup(fp_group, USRPROFILE_USER_NAME);
    }
    return USRPROFILECONFIG_OK;
}


int userProfileConfig::updateGroupAndUsr(sysFuncInterface* sysFuncImplimentation)
{
    sysFuncImpl = sysFuncImplimentation;
    //do nothing if root
    if(sysFuncImpl->Geteuid() == 0)
    {
        return 0;
    }

    int ret;
    FILE *fp_group = NULL;
    FILE *fp_password = NULL;

    fp_group  = sysFuncImpl->FileOpen(USRPROFILE_FILENAME_GROUP, USRPROFILE_READ_WRITE);
    if (fp_group == NULL)
    {
        TRS_ERR("failed to open file, line:%d, error[%s].\n", __LINE__, strerror(errno));
        ret = USRPROFILECONFIG_NOK;
    }
    else
    {
        fp_password = sysFuncImpl->FileOpen(USRPROFILE_FILENAME_PWD, USRPROFILE_READ_WRITE);
        if (fp_password == NULL)
        {
            TRS_ERR("failed to open file, line:%d, error[%s].\n", __LINE__, strerror(errno));
            ret = USRPROFILECONFIG_NOK;
        }
    }

    if(fp_group && fp_password)
    {
        //check if group exist, if not, create and add to /etc/group
        ret = updateGroupName(fp_group);

        //check if uid exist, if not, create and add to /etc/passwd
        ret += updateUserName(fp_password);
    }

    if(fp_group)
    {
        sysFuncImpl->FileClose(fp_group);
    }

    if(fp_password)
    {
        sysFuncImpl->FileClose(fp_password);
    }
    return ret;
};

main.cpp

#include <stdio.h>
#include <sysFuncInterfaceImpl.hpp>
#include "userprofileconfig.hpp"

int main(int argc,char** argv)
{
    userProfileConfig userprofileconfig;
    sysFuncInterfaceImpl sysFuncImpl;
    return userprofileconfig.updateGroupAndUsr((sysFuncInterface*)&sysFuncImpl);
}

跳过方案本身的讨论,在实现层面,调用了不少系统函数,如何做好UT呢?更具体的说,如何更容易mock 系统函数, 其实上面已经体现了方案。

UT 样例如下

userprofilesystemMock.hpp


#ifndef YOUR_OWN_USERPROFILESYSTEMMOCK_HPP
#define YOUR_OWN_USERPROFILESYSTEMMOCK_HPP

#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <cstring>
#include <gtest/gtest.h>
#include <gmock/gmock.h>
#include <sysFuncInterface.hpp>

class userProfileSystemMock: public sysFuncInterface
{
public:
    virtual ~userProfileSystemMock() { }
    MOCK_METHOD0(Getuid, uid_t());
    MOCK_METHOD0(Geteuid, uid_t(void));
    MOCK_METHOD0(Getgid, gid_t(void));
    MOCK_METHOD1(Getgrgid, struct group *(gid_t gid));
    MOCK_METHOD1(Getpwnam, struct passwd *(const char *));

    MOCK_METHOD2(FileOpen, FILE *( const char *filename, const char *mode));
    MOCK_METHOD1(FileClose, int(FILE *fp));
    MOCK_METHOD4(FileWrite, size_t(const void *ptr, size_t size, size_t nmemb, FILE *stream));
};


#endif //YOUR_OWN_USERPROFILESYSTEMMOCK_HPP

userprofileconfigTest.cpp

#include <userprofileconfig.hpp>
#include <userprofilesystemMock.hpp>

using namespace testing;

#define TEST_USERPROFILECONFIG_OK 0

class userprofileconfigTest: public ::testing::Test
{
public:
    userProfileSystemMock systemMock;
    userProfileConfig userprofileconfig;
protected:
    void SetUp() override
    {
    }

    void TearDown() override
    {
    }

    FILE* pwdnull = NULL;
    FILE* pwdok = (FILE*)0xFFFFFFFF;
    struct passwd *pwdInfo = (struct passwd*)0xFFFFFFFF;
    struct passwd *pwdInfonull = (struct passwd*)0;

    struct group *grpInfo = (struct group*)0xFFFFFFFF;
    struct group *grpnull = (struct group*)0;

    void expectCallInit()
    {
        EXPECT_CALL(systemMock, Getuid()).Times(1).WillRepeatedly(Return(1));
        EXPECT_CALL(systemMock, Geteuid()).Times(1).WillRepeatedly(Return(1));
        EXPECT_CALL(systemMock, Getgid()).Times(3).WillRepeatedly(Return(1));
        EXPECT_CALL(systemMock, Getgrgid(_)).Times(1).WillRepeatedly(Return(grpnull));
        EXPECT_CALL(systemMock, FileOpen(_, _)).Times(2).WillRepeatedly(Return(pwdok));
        EXPECT_CALL(systemMock, Getpwnam(_)).Times(1).WillOnce(Return(pwdInfonull));
        EXPECT_CALL(systemMock, FileClose(_)).Times(2).WillRepeatedly(Return(0));
    }
};

TEST_F(userprofileconfigTest, uidIsRootShouldReturnOK)
{
    EXPECT_CALL(systemMock, Geteuid()).Times(1).WillOnce(Return(0));

    EXPECT_EQ(TEST_USERPROFILECONFIG_OK, userprofileconfig.updateGroupAndUsr(&systemMock));
}

TEST_F(userprofileconfigTest, groupFileOpenFailedShouldReturnNOK)
{
    EXPECT_CALL(systemMock, Geteuid()).Times(1).WillRepeatedly(Return(1));
    EXPECT_CALL(systemMock, FileOpen(_, _)).Times(1).WillOnce(Return(pwdnull));

    EXPECT_NE(TEST_USERPROFILECONFIG_OK, userprofileconfig.updateGroupAndUsr(&systemMock));
}

TEST_F(userprofileconfigTest, passwdFileOpenFailedShouldReturnNOK)
{
    EXPECT_CALL(systemMock, Geteuid()).Times(1).WillRepeatedly(Return(1));
    EXPECT_CALL(systemMock, FileOpen(_, _)).Times(2).WillOnce(Return(pwdok)).WillOnce(Return(pwdnull));
    EXPECT_CALL(systemMock, FileClose(_)).Times(1).WillRepeatedly(Return(0));

    EXPECT_NE(TEST_USERPROFILECONFIG_OK, userprofileconfig.updateGroupAndUsr(&systemMock));
}

TEST_F(userprofileconfigTest, passwdAndgroupFileWriteFailedShouldReturnNOK)
{
    expectCallInit();
    EXPECT_CALL(systemMock, FileWrite(_, _, _, _)).Times(2).WillRepeatedly(Return(0));

    EXPECT_NE(TEST_USERPROFILECONFIG_OK, userprofileconfig.updateGroupAndUsr(&systemMock));
}

TEST_F(userprofileconfigTest, passwdAndgroupFileWriteSuccessShouldReturnOK)
{
    expectCallInit();
    EXPECT_CALL(systemMock, FileWrite(_, _, _, _)) .Times(2).WillOnce(Return(15)).WillOnce(Return(43));

    EXPECT_EQ(TEST_USERPROFILECONFIG_OK, userprofileconfig.updateGroupAndUsr(&systemMock));
}

TEST_F(userprofileconfigTest, userIsAlreadyExistShouldReturnOK)
{
    EXPECT_CALL(systemMock, Geteuid()).Times(1).WillRepeatedly(Return(1));
    EXPECT_CALL(systemMock, FileOpen(_, _)).Times(2).WillRepeatedly(Return(pwdok));
    EXPECT_CALL(systemMock, Getgid()).Times(1).WillRepeatedly(Return(1));
    EXPECT_CALL(systemMock, Getgrgid(_)).Times(1).WillRepeatedly(Return(grpInfo));
    EXPECT_CALL(systemMock, Getpwnam(_)).Times(1).WillOnce(Return(pwdInfo));
    EXPECT_CALL(systemMock, FileClose(_)).Times(2).WillRepeatedly(Return(0));

    EXPECT_EQ(TEST_USERPROFILECONFIG_OK, userprofileconfig.updateGroupAndUsr(&systemMock));
}

上面的mock 方式,大专家一开始开了也喊了一下: nice mock

makefile(是嵌入到一个大一些的subsystem里面,权当示例)



dist_bin_SCRIPTS = start-rcpYOUR_OWN

BASECPPFLAGS =  $(PTHREAD_CFLAGS) \
                -I$(top_srcdir)/config/userprofileconfig/include

BASELDFLAGS = $(PTHREAD_CFLAGS)

AM_CXXFLAGS = $(WARN_CXXFLAGS) -std=c++17

BASELIBS = $(PTHREAD_LIBS)

NAME = YOUR_OWN
userprofileinitbindir = @libexecdir@/$(NAME)

#binary
userprofileinitbin_PROGRAMS =\
    userprofileinit

userprofileinit_SOURCES = \
    userprofileconfig/src/userprofileconfig.cpp \
    userprofileconfig/src/main.cpp

userprofileinit_CPPFLAGS = $(BASECPPFLAGS)
userprofileinit_LDFLAGS = $(BASELDFLAGS)
userprofileinit_LDADD = $(BASELIBS)

####################################################################
######## UNIT Test #################################################
check_PROGRAMS = \
    testuserprofileinit

COMMONUT_LIBS = \
    $(PTHREAD_LIBS) \
    $(CODE_COVERAGE_LIBS) \
    $(GTEST_LIBS) \
    $(GMOCK_LIBS)

testuserprofileinit_SOURCES = \
    userprofileconfig/src/userprofileconfig.cpp \
    userprofileconfig/tst/userprofileconfigTest.cpp

testuserprofileinit_CPPFLAGS = \
    $(BASE_NO_WARNING_FLAGS) \
    -I$(top_srcdir)/config/userprofileconfig/include \
    -I$(top_srcdir)/config/userprofileconfig/tst

testuserprofileinit_CXXFLAGS = $(WARN_CXXFLAGS) $(CODE_COVERAGE_CXXFLAGS)
testuserprofileinit_LDFLAGS = $(PTHREAD_LDFLAGS)

testuserprofileinit_LDADD = \
    $(COMMONUT_LIBS)

TESTS = \
    testuserprofileinit

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值