GTest源码剖析(四)——TEST_P宏
来源:互联网 发布:南山数据恢复 编辑:程序博客网 时间:2024/06/03 17:41
GTest源码剖析——TEST_P宏
- GTest源码剖析TEST_P宏
- TEST_P宏用法
- TestWithParam 类
- 1 TestWithParam 类定义
- 2 WithParamInterface 模版类定义
- INSTANTIATE_TEST_CASE_P宏
- 1 INSTANTIATE_TEST_CASE_P宏展开
- 2 参数生成器
- 21 参数生成器Values
- 22 ParamGenerator模版类
- 23 TestParamInfo struct
- 24 GetParamNameGen
- 25 PrintToStringParamName
- 3 参数化测试用例信息注册
- 31 ParameterizedTestCaseRegistry类
- 32 ParameterizedTestCaseInfoBase类
- 33 ParameterizedTestCaseInfo类
- 34 TestMetaFactoryBase类
- TEST_P宏
- 1 TEST_P宏展开
- 测试用例信息注册的真正时机
- 1 UnitTestImplRegisterParameterizedTests
- 11 UnitTestImplRegisterParameterizedTests
- 12 ParameterizedTestCaseRegistryRegisterTests
- 13 ParameterizedTestCaseInfoRegisterTests
- 1 UnitTestImplRegisterParameterizedTests
- 参考
1 TEST_P宏用法
TEST_P宏在用法上比TEST/TEST_F宏强大很多。事实上,TEST_P宏的实现也复杂非常多。
在这里先简单介绍下TEST_P宏的使用,然后再根据它进行展开来分析源码。
//Step1:申明一个呼叫参数类,该类主要用于TEST_P宏中实现的测试逻辑使用class CallArgs{public: CallArgs(bool hasAudio,bool hasVideo): _hasAudio(hasAudio),_hasVideo(hasVideo){} bool audio(){ return _hasAudio;} bool video(){ return _hasVideo;}private: bool _hasAudio; bool _hasVideo;};//Step2:申明一个呼叫类,该类同时也是TEST_P宏的第一个参数test_case_name// 该类继承了TestWithParam<CallArgs>模版类,从而使得CallArgs类与Call类进行了关联。class Call: public ::testing::TestWithParam<CallArgs>{};//Step3: 使用INSTANTIATE_TEST_CASE_P宏,对Call类进行类相关多个的参数设置// 这里只添加了两个参数CallArgs(true,true)和CallArgs(true,false),事实上,可以添加多大50个参数。// 这里使用参数生成器::testing::Values,GTest定义了了很多参数生成器。INSTANTIATE_TEST_CASE_P(VOIP, Call, ::testing::Values( CallArgs(true,true), CallArgs(true,false) ) );//Step4: 编写了使用TEST_P宏实现的测试用例// 使用了TestWithParam<CallArgs>类的GetParam()接口获取参数CallArgs// 实际上这是两个测试用例,即该代码段会执行两个,参数分别为CallArgs(true,true)和CallArgs(true,false)TEST_P( Call, makeCall){ CallArgs args = GetParam(); ASSERT_TRUE( makeCall(args.audio(),args.video()) );}
2 TestWithParam 类
2.1 TestWithParam 类定义
该类仅仅是继承了Test类和WithParamInterface类,这里主要介绍一下WithParamInterface模版类。
template <typename T>class TestWithParam : public Test, public WithParamInterface<T> {};
2.2 WithParamInterface 模版类定义
该类定义了ParamType,用于参数型别推导(这方面可以参考STL源码剖析)。
提供了GetParam()函数,用于TST_P宏里的实现逻辑获取参数。
template <typename T>class WithParamInterface {public: typedef T ParamType; virtual ~WithParamInterface() {} const ParamType& GetParam() const { GTEST_CHECK_(parameter_ != NULL) << "GetParam() can only be called inside a value-parameterized test " << "-- did you intend to write TEST_P instead of TEST_F?"; return *parameter_; } private: static void SetParam(const ParamType* parameter) { parameter_ = parameter; } //不太理解为何声明为static,可能是为了节约内存的考虑? //申明为static后,需要保证每次运行到不同的测试用例时,其值能够匹配。 static const ParamType* parameter_; //申明了友元类ParameterizedTestFactory,使得其可以修改调用SetParam() template <class TestClass> friend class internal::ParameterizedTestFactory;};template <typename T>const T* WithParamInterface<T>::parameter_ = NULL;
3 INSTANTIATE_TEST_CASE_P宏
3.1 INSTANTIATE_TEST_CASE_P宏展开
- 定义一个获取参数生成器函数;
- 定义一个生成参数字符串的函数;
- 通过UnitTest::GetInstance()获取UnitTest类的单例;
- 通过parameterized_test_registry()获取UnitTest单例类的参数注册器ParameterizedTestCaseRegistry;
- 通过GetTestCasePatternHolder获取参数化测试用例的信息ParameterizedTestCaseInfo;
- 通过AddTestCaseInstantiation()添加TestCase的信息到GTest中。
#define INSTANTIATE_TEST_CASE_P(VOIP, Call, Values,... ) ) ParamGenerator<CallArgs> gtest_VOIPCall_EvalGenerator_() { return ValueArray2(); } std::string gtest_VOIPCall_EvalGenerateName_(const TestParamInfo<CallArgs>& info) { return GetParamNameGen<CallArgs>(info); } int gtest_VOIPCall_dummy_ = UnitTest::GetInstance()->parameterized_test_registry(). GetTestCasePatternHolder<Call>(Call, CodeLocation(__FILE__, __LINE__))-> AddTestCaseInstantiation(VOIP, >est_VOIPCall_EvalGenerator_, >est_VOIPCall_EvalGenerateName_, __FILE__, __LINE__)
3.2 参数生成器
3.2.1 参数生成器:Values
实际上Values是一组生成器,目前支持参数从1到50。
//Valuestemplate <typename T1, typename T2>internal::ValueArray2<T1, T2> Values(T1 v1, T2 v2) { return internal::ValueArray2<T1, T2>(v1, v2);}//ValueArray2 模版类//重载了operator ParamGenerator<T>() const;template <typename T1, typename T2>class ValueArray2 {public: ValueArray2(T1 v1, T2 v2) : v1_(v1), v2_(v2) {} template <typename T> operator ParamGenerator<T>() const { const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_)}; return ValuesIn(array); } private: void operator=(const ValueArray2& other); const T1 v1_; const T2 v2_;};
3.2.2 ParamGenerator模版类
template<typename T>class ParamGenerator {public: typedef ParamIterator<T> iterator; explicit ParamGenerator(ParamGeneratorInterface<T>* impl) : impl_(impl) {} ParamGenerator(const ParamGenerator& other) : impl_(other.impl_) {} ParamGenerator& operator=(const ParamGenerator& other) { impl_ = other.impl_; return *this; } iterator begin() const { return iterator(impl_->Begin()); } iterator end() const { return iterator(impl_->End()); }private: linked_ptr<const ParamGeneratorInterface<T> > impl_;};// ParamGeneratorInterface模版类template <typename T>class ParamGeneratorInterface {public: typedef T ParamType; virtual ~ParamGeneratorInterface() {} virtual ParamIteratorInterface<T>* Begin() const = 0; virtual ParamIteratorInterface<T>* End() const = 0;};
3.2.3 TestParamInfo struct
主要是固定参数及其index
template <class ParamType>struct TestParamInfo { TestParamInfo(const ParamType& a_param, size_t an_index) : param(a_param), index(an_index) {} ParamType param; size_t index;};
3.2.4 GetParamNameGen
//默认生成包含索引的字符串template <class ParamType>std::string DefaultParamName(const TestParamInfo<ParamType>& info) { Message name_stream; name_stream << info.index; return name_stream.GetString();}template <class ParamType, class ParamNameGenFunctor>ParamNameGenFunctor GetParamNameGen(ParamNameGenFunctor func) { return func;}template <class ParamType>struct ParamNameGenFunc { typedef std::string Type(const TestParamInfo<ParamType>&);};template <class ParamType>typename ParamNameGenFunc<ParamType>::Type *GetParamNameGen() { return DefaultParamName;}
3.2.5 PrintToStringParamName
struct PrintToStringParamName { template <class ParamType> std::string operator()(const TestParamInfo<ParamType>& info) const { return PrintToString(info.param); }};
3.3 参数化测试用例信息注册
int gtest_VOIPCall_dummy_ = UnitTest::GetInstance()->parameterized_test_registry(). GetTestCasePatternHolder<Call>(Call, CodeLocation(__FILE__, __LINE__))-> AddTestCaseInstantiation(VOIP, >est_VOIPCall_EvalGenerator_, >est_VOIPCall_EvalGenerateName_, __FILE__, __LINE__)
3.3.1 ParameterizedTestCaseRegistry类
class ParameterizedTestCaseRegistry {public: ParameterizedTestCaseRegistry() {} //析构时销毁资源,而这些资源是在GetTestCasePatternHolder函数中创建的 ~ParameterizedTestCaseRegistry() { TestCaseInfoContainer::iterator it = test_case_infos_.begin(); for ( ; it != test_case_infos_.end(); ++it) { delete *it; } } template <class TestCase> ParameterizedTestCaseInfo<TestCase>* GetTestCasePatternHolder( const char* test_case_name,CodeLocation code_location) { ParameterizedTestCaseInfo<TestCase>* typed_test_info = NULL; //遍历test_case_infos_查找test_case_name TestCaseInfoContainer::iterator it = test_case_infos_.begin(); for ( ; it != test_case_infos_.end(); ++it) { if ( (*it)->GetTestCaseName() == test_case_name) { //如果找到,对比TypeID是否一致;如果不一致则终止程序; if ((*it)->GetTestCaseTypeId() != GetTypeId<TestCase>()) { ReportInvalidTestCaseType(test_case_name, code_location); posix::Abort(); } else { // 强制类型转换 typed_test_info = CheckedDowncastToActualType<ParameterizedTestCaseInfo<TestCase> >(*it); } break; } } //如果找不到对应的test_case_name,则创建一个新的typed_test_info,并插入test_case_infos_中 if (typed_test_info == NULL) { typed_test_info = new ParameterizedTestCaseInfo<TestCase>(test_case_name, code_location); test_case_infos_.push_back(typed_test_info); } return typed_test_info; } //循环遍历test_case_infos,并调用TestCase的RegisterTests()函数。 void RegisterTests() { TestCaseInfoContainer::iterator it = test_case_infos_.begin(); for ( ; it != test_case_infos_.end(); ++it) { (*it)->RegisterTests(); } }private: typedef ::std::vector<ParameterizedTestCaseInfoBase*> TestCaseInfoContainer; TestCaseInfoContainer test_case_infos_; GTEST_DISALLOW_COPY_AND_ASSIGN_(ParameterizedTestCaseRegistry);};
3.3.2 ParameterizedTestCaseInfoBase类
class ParameterizedTestCaseInfoBase {public: virtual ~ParameterizedTestCaseInfoBase() {} virtual const std::string& GetTestCaseName() const = 0; virtual TypeId GetTestCaseTypeId() const = 0; virtual void RegisterTests() = 0; protected: ParameterizedTestCaseInfoBase() {} private: GTEST_DISALLOW_COPY_AND_ASSIGN_(ParameterizedTestCaseInfoBase);};
3.3.3 ParameterizedTestCaseInfo类
template <class TestCase>class ParameterizedTestCaseInfo : public ParameterizedTestCaseInfoBase {public: typedef typename TestCase::ParamType ParamType; typedef ParamGenerator<ParamType>(GeneratorCreationFunc)(); typedef typename ParamNameGenFunc<ParamType>::Type ParamNameGeneratorFunc; explicit ParameterizedTestCaseInfo(const char* name, CodeLocation code_location): test_case_name_(name), code_location_(code_location) {} virtual const std::string& GetTestCaseName() const { return test_case_name_; } //获取TestCase的ID,用于唯一标识作用, //在ParameterizedTestCaseRegistry::GetTestCasePatternHolder处有使用其进行容错判断 virtual TypeId GetTestCaseTypeId() const { return GetTypeId<TestCase>(); } /* TEST_P macro uses AddTestPattern() to record information about a single test in a LocalTestInfo structure. test_case_name is the base name of the test case (without invocation prefix). test_base_name is the name of an individual test without parameter index. For the test SequenceA/FooTest.DoBar/1 FooTest is test case base name and DoBar is test base name. */ void AddTestPattern(const char* test_case_name, const char* test_base_name, TestMetaFactoryBase<ParamType>* meta_factory) { tests_.push_back(linked_ptr<TestInfo>( new TestInfo(test_case_name,test_base_name,meta_factory))); } //仅仅是把信息插入nstantiations_中 int AddTestCaseInstantiation(const std::string& instantiation_name, GeneratorCreationFunc* func, ParamNameGeneratorFunc* name_func, const char* file, int line) { instantiations_.push_back(InstantiationInfo(instantiation_name, func, name_func, file, line)); return 0; } virtual void RegisterTests() { //这部分代码比较多,这里先不展开。 //此处的作用是真正的注册测试用例信息。 //其在InitGoogleTest接口里才会得到调用。 //详细参考 <<5.1.3 ParameterizedTestCaseInfo::RegisterTests()>> } // RegisterTestsprivate: struct TestInfo { TestInfo(const char* a_test_case_base_name, const char* a_test_base_name, TestMetaFactoryBase<ParamType>* a_test_meta_factory) : test_case_base_name(a_test_case_base_name), test_base_name(a_test_base_name), test_meta_factory(a_test_meta_factory) {} const std::string test_case_base_name; const std::string test_base_name; const scoped_ptr<TestMetaFactoryBase<ParamType> > test_meta_factory; }; typedef ::std::vector<linked_ptr<TestInfo> > TestInfoContainer; struct InstantiationInfo { InstantiationInfo(const std::string &name_in, GeneratorCreationFunc* generator_in, ParamNameGeneratorFunc* name_func_in, const char* file_in, int line_in) : name(name_in), generator(generator_in), name_func(name_func_in), file(file_in), line(line_in) {} std::string name; GeneratorCreationFunc* generator; ParamNameGeneratorFunc* name_func; const char* file; int line; }; typedef ::std::vector<InstantiationInfo> InstantiationContainer; static bool IsValidParamName(const std::string& name) { if (name.empty()) { return false; } for (std::string::size_type index = 0; index < name.size(); ++index) { if (!isalnum(name[index]) && name[index] != '_') { return false; } } return true; } const std::string test_case_name_; CodeLocation code_location_; TestInfoContainer tests_; InstantiationContainer instantiations_; GTEST_DISALLOW_COPY_AND_ASSIGN_(ParameterizedTestCaseInfo);};
3.3.4 TestMetaFactoryBase类
简单的工厂类
template <class ParamType>class TestMetaFactoryBase {public: virtual ~TestMetaFactoryBase() {} virtual TestFactoryBase* CreateTestFactory(ParamType parameter) = 0;};
4 TEST_P宏
4.1 TEST_P宏展开
eg 如前所述:
TEST_P( Call, makeCall){ CallArgs args = GetParam(); ASSERT_TRUE( makeCall(args.audio(),args.video()) );}
展开如下:
定义了一个继承Call的Call_makeCall_Test拼接类。
与TEST宏不同的是:
1. TEST_P宏中的TestBody()函数为public;而TEST宏中的TestBody()函数为private;
2. TEST_P宏中定义了AddToRegistry()类成员用于注册信息,
而TEST宏是使用MakeAndRegisterTestInfo()接口进行信息注册。
# define TEST_P(Call, makeCall)// Step1:申明一个拼接类class Call_makeCall_Test : public Call { public: Call_makeCall_Test {} virtual void TestBody(); private: //1: 通过UnitTest::GetInstance()获取UnitTest类的单例; //2: 通过parameterized_test_registry()获取UnitTest单例类的ParameterizedTestCaseRegistry; //3: 通过GetTestCasePatternHolder获取参数化测试用例的信息ParameterizedTestCaseInfo; // 因为在INSTANTIATE_TEST_CASE_P处调用时已创建ParameterizedTestCaseInfo,故此次仅是获取; //4: 通过ParameterizedTestCaseInfo类的AddTestPattern函数添加TestCase的信息到GTest中。 // 与INSTANTIATE_TEST_CASE_P宏调用AddTestCaseInstantiation函数不同的是, // 这里调用AddTestPattern函数添加了具体的测试用例信息,即如“makeCall”。 static int AddToRegistry() { ::testing::UnitTest::GetInstance()->parameterized_test_registry(). GetTestCasePatternHolder<Call>( Call, CodeLocation(__FILE__, __LINE__))-> AddTestPattern(Call,makeCall,new TestMetaFactory<Call_makeCall_Test>()); return 0; } static int gtest_registering_dummy_ ; GTEST_DISALLOW_COPY_AND_ASSIGN_(Call_makeCall_Test); }; // Step2:注册测试用例信息int Call_makeCall_Test::gtest_registering_dummy_ = Call_makeCall_Test::AddToRegistry(); // Step3:对TestBody()进行实现void Call_makeCall_Test::TestBody(){ CallArgs args = GetParam(); ASSERT_TRUE( makeCall(args.audio(),args.video()) );}
5 测试用例信息注册的真正时机
事实上,到目前为止,测试用例的信息仍未真正的注册。
TEST宏比较分立,所以它是有一个用例注册一次。与TEST/TEST_P宏不同的是,TEST_P宏需要和INSTANTIATE_TEST_CASE_P宏进行合作,所以其实际上真正注册信息的时机在initGoogleTest函数里实现的。因为initGoogleTest函数在main函数里面调用,此时TEST_P宏和INSTANTIATE_TEST_CASE_P宏的信息已经全部加载完毕,此时再统一进行信息注册。
InitGoogleTest();==>InitGoogleTestImpl(); ==>GetUnitTestImpl()->PostFlagParsingInit(); ==>UnitTestImpl::RegisterParameterizedTests();
5.1 UnitTestImpl::RegisterParameterizedTests()
5.1.1 UnitTestImpl::RegisterParameterizedTests()
通过parameterized_tests_registered_确保该函数只调用一次
其实现是通过调用ParameterizedTestCaseRegistry类的RegisterTests()函数实现。
void UnitTestImpl::RegisterParameterizedTests() {#if GTEST_HAS_PARAM_TEST if (!parameterized_tests_registered_) { parameterized_test_registry_.RegisterTests(); parameterized_tests_registered_ = true; }#endif}
5.1.2 ParameterizedTestCaseRegistry::RegisterTests()
- 在ParameterizedTestCaseRegistry类中,所有测试用例信息都保存在test_case_infos_,是INSTANTIATE_TEST_CASE_P宏调用GetTestCasePatternHolder函数,创建并保存的信息数据。
ParameterizedTestCaseRegistry::RegisterTests()的实现是,循环遍历test_case_infos_数组,并逐次调用ParameterizedTestCaseInfoBase::RegisterTests()函数进行注册。
void ParameterizedTestCaseRegistry::RegisterTests() { //typedef ::std::vector<ParameterizedTestCaseInfoBase*> TestCaseInfoContainer; TestCaseInfoContainer::iterator it = test_case_infos_.begin(); for ( ; it != test_case_infos_数组,.end(); ++it) { (*it)->RegisterTests(); }}
5.1.3 ParameterizedTestCaseInfo::RegisterTests()
事实上,ParameterizedTestCaseInfoBase是个虚类,提供相关接口,在GTest主要是ParameterizedTestCaseInfo类进行实现。我们真实的注册也是通过ParameterizedTestCaseInfo::RegisterTests()接口完成的。
virtual void ParameterizedTestCaseInfo::RegisterTests() { //循环遍历tests_,tests_值是在TEST_P调用AddTestPattern函数添加的。 typename TestInfoContainer::iterator test_it = tests_.begin(); for ( ; test_it != tests_.end(); ++test_it) { linked_ptr<TestInfo> test_info = *test_it; typename InstantiationContainer::iterator gen_it = instantiations_.begin(); //循环遍历instantiations_ //其值是INSTANTIATE_TEST_CASE_P宏调用AddTestCaseInstantiation()函数添加的。 for ( ; gen_it != instantiations_.end(); ++gen_it) { const std::string& instantiation_name = gen_it->name; ParamGenerator<ParamType> generator((*gen_it->generator)()); ParamNameGeneratorFunc* name_func = gen_it->name_func; const char* file = gen_it->file; int line = gen_it->line; //此处进行拼接test_case_name; //eg:instantiation_name :"VOIP" // :test_info->test_case_base_name: "Call" // ===>test_case_name = "VOIP/Call" std::string test_case_name; if ( !instantiation_name.empty() ) { test_case_name = instantiation_name + "/"; } test_case_name += test_info->test_case_base_name; size_t i = 0; std::set<std::string> test_param_names; //循环遍历参数生成器,并拼接测试用例名 //eg: ==> "makeCall/0" ==> "makeCall/1" typename ParamGenerator<ParamType>::iterator param_it = generator.begin(); for ( ; param_it != generator.end(); ++param_it, ++i) { Message test_name_stream; std::string param_name = name_func(TestParamInfo<ParamType>(*param_it, i)); GTEST_CHECK_(IsValidParamName(param_name)) << "Parameterized test name '" << param_name << "' is invalid, in " << file << " line " << line << std::endl; GTEST_CHECK_(test_param_names.count(param_name) == 0) << "Duplicate parameterized test name '" << param_name << "', in " << file << " line " << line << std::endl; test_param_names.insert(param_name); test_name_stream << test_info->test_base_name << "/" << param_name; //调用MakeAndRegisterTestInfo进行信息注册,该函数分析详见《GTest源码剖析——TEST宏》处 MakeAndRegisterTestInfo( test_case_name.c_str(), test_name_stream.GetString().c_str(), NULL, // No type parameter. PrintToString(*param_it).c_str(), code_location_, GetTestCaseTypeId(), TestCase::SetUpTestCase, TestCase::TearDownTestCase, test_info->test_meta_factory->CreateTestFactory(*param_it)); } } } } // RegisterTests
如上,正式完成了TEST_P宏的信息注册。
6 参考
github: googletest
ZhaiPillar
2017-09-16
- GTest源码剖析(四)——TEST_P宏
- GTest源码剖析(二)——TEST宏
- GTest源码剖析(三)——TEST_F宏
- GTest源码剖析(一)——概述
- GTest源码剖析(六)——RUN_ALL_TESTS
- GTest源码剖析(七)——断言
- GTest源码剖析(五)——传入参数分析及InitGoogleTest
- 《STL源码剖析》—— 空间配置器(四)
- Azureus源码剖析(四)
- chromium源码剖析(四)
- Atlas源码剖析(四)
- nginx源码剖析(四) ——两个用来对齐的宏
- 《STL源码剖析》学习笔记之四——序列式容器(deque之一)
- udhcp源码剖析(四)——DHCP服务器的superloop
- Mina2.0框架源码剖析(四)
- Mina2.0框架源码剖析(四)
- Mina2.0框架源码剖析(四)
- Notepad++源码剖析(四)
- Android中的手势
- 1001. 害死人不偿命的(3n+1)猜想 (15)
- 静态顺序表的实现(增删查改排序)
- GTest源码剖析(三)——TEST_F宏
- caffe2之operator介绍(上)
- GTest源码剖析(四)——TEST_P宏
- 机器学习实战:K近邻算法(源码分析)
- 设计模式----导论
- cmd命令大全
- json递归查找key对应的值
- pyhon学习笔记--lambda
- Java用户登陆界面
- GTest源码剖析(五)——传入参数分析及InitGoogleTest
- 单链表的各种操作(增删查改)