最近因为项目逻辑方面都做的差不多了,于是赶紧去做做测试工作,免得项目上线之后出问题,于是打算用配置的形式做一个类似与白盒测试工具的东西出来。

因为项目使用pb来做协议通讯,所以配置的xml也是类似于pb,将pb的字段和类型配置进去,然后加上值,一个协议结构就可以了,现在只能通过修改值来做测试,后面会改动的更智能化一些,例如某个行为的次数,某个行为更随机等等。

去读了一下陈硕的关于pb处理协议的反射,学到了不少东西,同时对pb的一些东西理解更深刻了,google还是大牛很多。

1.如何处理pb的反射,通过协议字串动态生成一个协议

pb提供了一个强大的DescriptorPool::generated_pool()

代码如下:

    inline google::protobuf::Message* CreateMessage(const std::string& msg)
    {
        google::protobuf::Message* message = NULL;
        const google::protobuf::Descriptor* descriptor = google::protobuf::DescriptorPool::generated_pool()->FindMessageTypeByName("ProtoMsg." + msg);
        if (descriptor)
        {
            const google::protobuf::Message* prototype = google::protobuf::MessageFactory::generated_factory()->GetPrototype(descriptor);
            if (prototype)
            {
                message = prototype->New();
            }
        }

        return message;
    }

    inline void ReleaseMessage(google::protobuf::Message* pMsg)
    {
        if (NULL != pMsg)
        {
            pMsg->Clear();
            delete pMsg;
        }
    }

这两个函数可以动态生成pb的message,其中ProtoMsg是你pb package的名字

 

2.通过反射将配置中的值设置进pb字段

pb的Message基类提供了一个Reflection,这个类非常强大

代码如下

    // mstrCurMsg 当前正在执行的协议
    google::protobuf::Message* pMsg = Test::CreateMessage(mstrCurMsg);
    const google::protobuf::Descriptor* pDescriptor = google::protobuf::DescriptorPool::generated_pool()->FindMessageTypeByName("ProtoMsg." + mstrCurMsg);
    assert(NULL != pDescriptor);
	
	// 这一个可以获取到反射类,然后可以将配置中值赋值进去
    const google::protobuf::Reflection* pReflection = pMsg->GetReflection();
    assert(NULL != pReflection);

    for (int i = 0; i < pDescriptor->field_count(); ++i)
    {
        const google::protobuf::FieldDescriptor* pFieldDescriptor = pDescriptor->field(i);
        assert(NULL != pFieldDescriptor);

        const std::string& strFieldName = pFieldDescriptor->name();
        
        const TestConfigModule::MsgEntry* pMsgEntry = pMsgStruct->GetMsgEntry(strFieldName);
        assert(NULL != pMsgEntry);

        // 读取字段类型,顺带可以做类型检查
        assert(pMsgEntry->mnType == pFieldDescriptor->type());

        // 设置值
        switch (pMsgEntry->mnType)
        {
        case Test::TYPE_STRING:
            pReflection->SetString(pMsg, pFieldDescriptor, pMsgEntry->mstrValue);
            break;
		// ...
        default:
            break;
        }
    }

    std::string strData;
    if (!pMsg->SerializeToString(&strData))
    {
        m_pLogModule->LogNormal("Test stop, cannot SerializeToString ", mstrCurMsg, __FUNCTION__, __LINE__);
        return;
    }

    Test::ReleaseMessage(pMsg);

通过这样的步骤,就可以自动创建message和对field赋值了,如果你也有pb的使用经验和技巧,欢迎分享

21 对 “protobuf通过反射来赋值”的想法;

  1. 最近写了一个协议调试工具,通过协议文件,在界面上填充字段就可以发送协议了。用的就是这个反射机制。

      1. 好像还是继续错.. “Backslashes, consecutive slashes, “.”, or “..” are not allowed in the virtual path”我觉得可能有什么参数可以设置proto的ROOT…但是好像不太好找..

          1. 我又更新了下,add subdirectory后发现那个include path问题解决了..不过还有些问题… error: no member named ‘protobuf_AddDesc_common_2ffoo_2ffoo_2eproto’ in the global namespace啊….略困难啊…多有麻烦了

            1. 貌似你没生成pb的cc和h文件吧,我在windows下用cmake gui试下来是cmake的lib和include没找到,我手动设置之后,直接是没有任何问题的

发表评论

电子邮件地址不会被公开。 必填项已用*标注