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

北城余情 提交于 2019-12-13 07:51:04

问题


Right now, IMO, Google typed-parameterized tests are annoying. You have to do:

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);

Much of this stuff seems it could be automated. For example:

#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);

(where those macros could be easily expanded to a very large size and then they will be sufficient for most use)

This works, however, this syntax is rather abnormal, so I'd like to make it more normal looking so it is more readable. This requires a way of doing something like this:

#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

which I'm not sure is possible to do. Is this possible?

I would simply post the last bit of code but others seem to think that it is too obscure to imagine a use case so I wanted to provide that as well.


回答1:


I have run into your problem using gtest and celero. I use a combination of macros and a python script to automate much of the boiler plate code.

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();                 \
 };

and i use it like this

`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

at the end you can see where the python script comes in. it parses the information in the comments and generates all the TEST_F(****) code by iteration though all possible cominations of keys and putting it into the include file. the python script also overcomes the issues with macros and templates in c++, namely, if you have TEST_F(example,test_name){test_code();}; the preprocessor will think there are three paramaters in TEST_F so you need to write it like this typedef example class_type_1_type_2; TEST_F(class_type_1_type_2, test_name){test_code();}; (in to tell the python script to pull something into a type def just put the type between two $ like in the example above) you call the python script like 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()

im sorry if the long post is annoying but i spent allot of time trying to speed up writing unit test and benchmarks and i hope these things can help you as well, WARNING: the python script is probably note the best written in the world but works for what i need it for. if you have anyquest about how to use this come feel free to ask me more or if you need it to do something it doesnt do i could add it to it. im working on putting up on github.



来源:https://stackoverflow.com/questions/32082262/using-macros-on-functions-in-an-array-to-make-gtest-typed-parameterized-tests-mo

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!