在数组中的函数上使用宏使gtest类型参数化测试更简洁

Using macros on functions in an array to make gtest typed-parameterized tests more succinct

本文关键字:类型参数 gtest 测试 简洁 数组 函数      更新时间:2023-10-16

现在,在我看来,Google类型参数化测试很烦人。你必须这样做:

template <typename fixtureType>
class testFixtureOld : public ::testing::Test
{
};
// Tell google test that we want to test this fixture
TYPED_TEST_CASE_P(testFixtureOld);

// Create the tests using this fixture
TYPED_TEST_P(testFixtureOld, OIS1Old)
{
  TypeParam n = 0;
  EXPECT_EQ(n, 0);
}
TYPED_TEST_P(testFixtureOld, OIS2Old)
{
  TypeParam n = 0;
  EXPECT_EQ(n, 0);
}
// Register the tests we just made
REGISTER_TYPED_TEST_CASE_P(testFixtureOld, OIS1Old, OIS2Old);

// Run the tests
typedef ::testing::Types<char, int, unsigned int> TypesTestingOld;
INSTANTIATE_TYPED_TEST_CASE_P(RunOldTests, testFixtureOld, TypesTestingOld);

这些东西似乎都可以自动化。例如:

#define TYPED_TESTS_P(fixture, testName1, test1, testName2, test2) TYPED_TEST_CASE_P(fixture); TYPED_TEST_P(fixture, testName1) test1 TYPED_TEST_P(fixture, testName2) test2 REGISTER_TYPED_TEST_CASE_P(fixture, testName1, testName2);
#define RUN_TYPED_TESTS_P(testSuiteName, fixture, type1, type2, type3) typedef::testing::Types<type1, type2, type3> TypesTesting; INSTANTIATE_TYPED_TEST_CASE_P(testSuiteName, fixture, TypesTesting);

template <typename fixtureType>
class testFixtureNew : public ::testing::Test
{
};
// Make our tests. This tells google test that we want to test this fixture,
// creates the tests using this fixture, and registers them.
TYPED_TESTS_P(testFixtureNew,
OISNew,
{
  TypeParam n = 0;
  EXPECT_EQ(n, 0);
},
OIS2New,
{
  TypeParam n = 0;
  EXPECT_EQ(n, 0);
}
)
// Run the tests
RUN_TYPED_TESTS_P(RunNewTests, testFixtureNew, char, int, unsigned int);

(这些宏可以很容易地扩展到一个非常大的大小,然后它们将足以用于大多数用途)

这个工作,然而,这个语法是相当不正常的,所以我想使它看起来更正常,这样它更可读。这需要这样一种方式:

#include <std>
using namespace std;
#define PassIntoThenListOut(inArg, fun1, fun2) something
PassIntoThenListOut(6,
int a(int foo)
{
  cout << "5+first = " << (5+foo);
},
int b(int bar)
{
  cout << "10+second = " << (10+bar);
}
)
// Should output:
// 5+first = 11
// 10+second = 16
// ArgumentNames: foo bar

我不确定是否有可能做到。这可能吗?

我将简单地发布最后一点代码,但其他人似乎认为它太模糊了,无法想象一个用例,所以我也想提供它。

我使用gtest和celero遇到了您的问题。我使用宏和python脚本的组合来自动化大部分锅炉板代码。

here are the macros i use
#define DEFINE(name,threads)  
     void name();             
     REGISTER(name,threads)   
#define REGISTER(name,threads)          
     SINGLE(name)                       
     THREADED(name,threads)             
#define THREADED(name, num_of_threads)            
 void name##Threaded(){                       
      std::vector< std::thread > threads;          
      for(int i=0; i<num_of_threads; i++){         
           threads.push_back( std::thread([this](){this->name##Single();}));     
      };                                           
      for(auto &t : threads){                      
           t.join();                               
      };                                           
 };
#define SINGLE(name)               
 void name##Single(){          
      this->name();                 
 };

我这样使用

`template<typename T>
 class LocationTest : public ::testing::Test{
      protected:
      location<T> policy;
      DEFINE(mallocfreetest,   LOCATION_THREADS)
      DEFINE(copytest,         LOCATION_THREADS)
 };'
template<typename T>
void LocationTest<T>::mallocfreetest(){
     void* p=NULL;
     p=policy.New(10);
     policy.Delete(p);
     EXPECT_TRUE(p);
};
template<typename T>
void LocationTest<T>::copytest(){
 int a=1;
 int* a_ptr=&a;
 int b=0;
 int* b_ptr=&b;
 policy.MemCopy(a_ptr,b_ptr,sizeof(int));
 EXPECT_EQ(1,b);
};
template<>
void LocationTest<device>::copytest(){
 size_t size=sizeof(int);
 int a=1;
 int* a_d=static_cast<int*>( policy.New(size) );
 ASSERT_TRUE(a_d);
 int b=0;
 int* b_d=static_cast<int*>( policy.New(size) );
 ASSERT_TRUE(b_d);
 cudaMemcpy(a_d,&a,size,cudaMemcpyHostToDevice);
 cudaMemcpy(b_d,&b,size,cudaMemcpyHostToDevice);
 policy.MemCopy(a_d,b_d,size);
 cudaMemcpy(&b,b_d,size,cudaMemcpyDeviceToHost);
 EXPECT_EQ(1,b);
};
#define HOST host
#define UNIFIED unified
#define DEVICE device
#define PINNED pinned
//python:key:policy=HOST UNIFIED DEVICE PINNED
//python:key:tests=copytestSingle mallocfreetestSingle copytestThreaded mallocfreetestThreaded
//python:template=TEST_F($LocationTest<|policy|>$,|tests|){this->|tests|();}
//python:start
//python:include=location.test
#include"location.test"
//python:end
#undef HOST
#undef UNIFIED
#undef DEVICE
#undef PINNED
#undef LOCATION_THREADS

末尾,您可以看到python脚本的位置。它解析注释中的信息,并通过所有可能的键组合迭代生成所有TEST_F(****)代码,并将其放入include文件中。python脚本还克服了c++中宏和模板的问题,即,如果您有TEST_F(例如,test_name) {test_code ();};预处理器会认为TEST_F中有三个参数所以你需要这样写typepedef example class_type_1_type_2;TEST_F (class_type_1_type_2 test_name) {test_code ();};(要告诉python脚本将某些内容拉入类型def中,只需将类型放在两个$之间,就像上面的例子一样)可以这样调用python脚本cover .py -i test_file.cpp

import os
import re
import copy
import getopt
import sys
#find functions,types,locations list;
def getoutputfile():
 global contents
 functionRegex=re.compile(r"//python:include=(.*)")
 fun=functionRegex.findall(contents)
 return fun[0]
def getkey():
 global contents
 params={}
 functionRegex=re.compile(r"//python:key:(w*)=(.*)")
 fun=functionRegex.findall(contents)
 for i in range( len(fun) ):
      params[ fun[i][0] ]=fun[i][1].split(" ")
 return params
def get_template():
 global contents
 functionRegex=re.compile(r"//python:template=(.*)")
 fun=functionRegex.findall(contents)
 return fun
def getnumlines(array,temp):
 num=1
 for i in array:
      num*=i
 return num*len(temp)
def initializeMaxArray(array):
 global keys
 global paramaters
 for i in range(keys):
      j=paramaters.keys()[i]
      array[i]=len( paramaters[j])
def increment(a,k):
 if k<keys:
      a[k]+=1
      if a[k]>=max_array[k]:
           a[k]=0
           a=increment(a,k+1)
# *******************read in file and data
a,b=getopt.getopt(sys.argv[1:],"i:")
input_file=a[0][1]
source_file=open(input_file,"r")
contents=source_file.read()
source_file.close()
#*****************initalize varaibles
paramaters=getkey()
template=get_template()
keys=len( paramaters.keys() )
max_array=[0]*keys
initializeMaxArray(max_array)
lines=getnumlines(max_array,template)
contents_new=[]
for i in range(len(template)):
   contents_new+=[template[i]]*(lines/len(template))
for i in range(len(contents_new)):
 contents_new[i]+='n'
temps=len(template)
array=[[0]*keys]*(lines*temps)
for i in range(lines-1):
 array[i+1]=copy.copy(array[i])
 increment(array[i+1],0)
#variable replacement
for j in range(lines):
  for i in range(keys):
      key=paramaters.keys()[i]
      x=array[j][i]
      result=contents_new[j].replace("|"+key+"|",paramaters[key][x])
      contents_new[j]=result
#typedef insertion

typedef_list=[];
typedefreg=re.compile(r".*$(.+)$.*")
for k in range(len( contents_new) ):
 matches=typedefreg.findall(contents_new[k] )
 for j in matches:
      match=j
      clear={"<":"_",">":"_",",":"_"," ":""}
      for i in clear.keys():
           match= match.replace(i,clear[i] )
 for j in matches:
      typedef=r"typedef "+j+" "+match+"; n"                                                                                rep="$"+j+"$"
      contents_new[k]=contents_new[k].replace(rep,match)
      typedef_list.append(typedef)
contents_new.insert(0,"//Tests/benchmarks n")
typedef_list.insert(0,"//typedefs n")
output=typedef_list+contents_new
outputfile=getoutputfile()
#write out to file
destination_file=open(outputfile,'w')
destination_file.write( "".join(output) )
destination_file.close()
我很抱歉,如果长帖子是烦人的,但我花了很多时间试图加快编写单元测试和基准,我希望这些东西可以帮助你,以及。警告:python脚本可能是世界上最好的,但我需要它的工作。如果你对如何使用这个有任何疑问,请随时问我更多,或者如果你需要它做一些它不做的事情,我可以把它添加到它。我正在努力把在github。