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

时间:2015-08-18 20:45:32

标签: c++ macros c-preprocessor googletest

目前,IMO,谷歌类型参数化测试很烦人。你必须这样做:

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

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

我只想发布最后一段代码,但其他人似乎认为想象一个用例太晦涩,所以我也想提供它。

1 个答案:

答案 0 :(得分:1)

我使用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(****)代码,尽管所有可能的键组合并将其放入包含文件中。 python脚本还克服了c ++中宏和模板的问题,即如果你有的话     TEST_F(例如,TEST_NAME){test_code();}; 预处理器会认为TEST_F中有三个参数 所以你需要这样写     typedef example class_type_1_type_2;     TEST_F(class_type_1_type_2,test_name){test_code();}; (告诉python脚本将某些内容拉入类型def,只需将类型放在两个$之间,如上例所示) 你调用python脚本就好     coverage.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。