Googletest export

Add the possibility of specifying the name in type parameterized tests.

Similar to how the last parameter of INSTANTIATE_TEST_CASE_P allows to override the name for (non-type) parametrized tests, this adds the possibility of adding a parameter to INSTANTIATE_TYPED_TEST_CASE_P. The argument has to be a class, which contains a static templated function GetName<T>(int), returning the name for type T.

PiperOrigin-RevId: 210532231
This commit is contained in:
Abseil Team 2018-08-28 09:40:18 -04:00 committed by Gennadiy Civil
parent 52f8183e7f
commit 03867b5389
4 changed files with 246 additions and 52 deletions

View File

@ -83,6 +83,24 @@ TYPED_TEST(FooTest, DoesBlah) {
TYPED_TEST(FooTest, HasPropertyA) { ... } TYPED_TEST(FooTest, HasPropertyA) { ... }
// TYPED_TEST_CASE takes an optional third argument which allows to specify a
// class that generates custom test name suffixes based on the type. This should
// be a class which has a static template function GetName(int index) returning
// a string for each type. The provided integer index equals the index of the
// type in the provided type list. In many cases the index can be ignored.
//
// For example:
// class MyTypeNames {
// public:
// template <typename T>
// static std::string GetName(int) {
// if (std::is_same<T, char>()) return "char";
// if (std::is_same<T, int>()) return "int";
// if (std::is_same<T, unsigned int>()) return "unsigned_int";
// }
// };
// TYPED_TEST_CASE(FooTest, MyTypes, MyTypeNames);
#endif // 0 #endif // 0
// Type-parameterized tests are abstract test patterns parameterized // Type-parameterized tests are abstract test patterns parameterized
@ -144,6 +162,11 @@ INSTANTIATE_TYPED_TEST_CASE_P(My, FooTest, MyTypes);
// If the type list contains only one type, you can write that type // If the type list contains only one type, you can write that type
// directly without Types<...>: // directly without Types<...>:
// INSTANTIATE_TYPED_TEST_CASE_P(My, FooTest, int); // INSTANTIATE_TYPED_TEST_CASE_P(My, FooTest, int);
//
// Similar to the optional argument of TYPED_TEST_CASE above,
// INSTANTIATE_TEST_CASE_P takes an optional fourth argument which allows to
// generate custom names.
// INSTANTIATE_TYPED_TEST_CASE_P(My, FooTest, MyTypes, MyTypeNames);
#endif // 0 #endif // 0
@ -160,12 +183,19 @@ INSTANTIATE_TYPED_TEST_CASE_P(My, FooTest, MyTypes);
// given test case. // given test case.
# define GTEST_TYPE_PARAMS_(TestCaseName) gtest_type_params_##TestCaseName##_ # define GTEST_TYPE_PARAMS_(TestCaseName) gtest_type_params_##TestCaseName##_
// Expands to the name of the typedef for the NameGenerator, responsible for
// creating the suffixes of the name.
#define GTEST_NAME_GENERATOR_(TestCaseName) \
gtest_type_params_##TestCaseName##_NameGenerator
// The 'Types' template argument below must have spaces around it // The 'Types' template argument below must have spaces around it
// since some compilers may choke on '>>' when passing a template // since some compilers may choke on '>>' when passing a template
// instance (e.g. Types<int>) // instance (e.g. Types<int>)
# define TYPED_TEST_CASE(CaseName, Types) \ # define TYPED_TEST_CASE(CaseName, Types, ...) \
typedef ::testing::internal::TypeList< Types >::type \ typedef ::testing::internal::TypeList< Types >::type GTEST_TYPE_PARAMS_( \
GTEST_TYPE_PARAMS_(CaseName) CaseName); \
typedef ::testing::internal::NameGeneratorSelector<__VA_ARGS__>::type \
GTEST_NAME_GENERATOR_(CaseName)
# define TYPED_TEST(CaseName, TestName) \ # define TYPED_TEST(CaseName, TestName) \
template <typename gtest_TypeParam_> \ template <typename gtest_TypeParam_> \
@ -179,13 +209,19 @@ INSTANTIATE_TYPED_TEST_CASE_P(My, FooTest, MyTypes);
bool gtest_##CaseName##_##TestName##_registered_ GTEST_ATTRIBUTE_UNUSED_ = \ bool gtest_##CaseName##_##TestName##_registered_ GTEST_ATTRIBUTE_UNUSED_ = \
::testing::internal::TypeParameterizedTest< \ ::testing::internal::TypeParameterizedTest< \
CaseName, \ CaseName, \
::testing::internal::TemplateSel< \ ::testing::internal::TemplateSel<GTEST_TEST_CLASS_NAME_(CaseName, \
GTEST_TEST_CLASS_NAME_(CaseName, TestName)>, \ TestName)>, \
GTEST_TYPE_PARAMS_(CaseName)>::Register(\ GTEST_TYPE_PARAMS_( \
"", ::testing::internal::CodeLocation(__FILE__, __LINE__), \ CaseName)>::Register("", \
#CaseName, #TestName, 0); \ ::testing::internal::CodeLocation( \
__FILE__, __LINE__), \
#CaseName, #TestName, 0, \
::testing::internal::GenerateNames< \
GTEST_NAME_GENERATOR_(CaseName), \
GTEST_TYPE_PARAMS_(CaseName)>()); \
template <typename gtest_TypeParam_> \ template <typename gtest_TypeParam_> \
void GTEST_TEST_CLASS_NAME_(CaseName, TestName)<gtest_TypeParam_>::TestBody() void GTEST_TEST_CLASS_NAME_(CaseName, \
TestName)<gtest_TypeParam_>::TestBody()
#endif // GTEST_HAS_TYPED_TEST #endif // GTEST_HAS_TYPED_TEST
@ -250,15 +286,19 @@ INSTANTIATE_TYPED_TEST_CASE_P(My, FooTest, MyTypes);
// The 'Types' template argument below must have spaces around it // The 'Types' template argument below must have spaces around it
// since some compilers may choke on '>>' when passing a template // since some compilers may choke on '>>' when passing a template
// instance (e.g. Types<int>) // instance (e.g. Types<int>)
# define INSTANTIATE_TYPED_TEST_CASE_P(Prefix, CaseName, Types) \ # define INSTANTIATE_TYPED_TEST_CASE_P(Prefix, CaseName, Types, ...) \
bool gtest_##Prefix##_##CaseName GTEST_ATTRIBUTE_UNUSED_ = \ bool gtest_##Prefix##_##CaseName GTEST_ATTRIBUTE_UNUSED_ = \
::testing::internal::TypeParameterizedTestCase<CaseName, \ ::testing::internal::TypeParameterizedTestCase< \
GTEST_CASE_NAMESPACE_(CaseName)::gtest_AllTests_, \ CaseName, GTEST_CASE_NAMESPACE_(CaseName)::gtest_AllTests_, \
::testing::internal::TypeList< Types >::type>::Register(\ ::testing::internal::TypeList< Types >::type>:: \
#Prefix, \ Register(#Prefix, \
::testing::internal::CodeLocation(__FILE__, __LINE__), \ ::testing::internal::CodeLocation(__FILE__, __LINE__), \
&GTEST_TYPED_TEST_CASE_P_STATE_(CaseName), \ &GTEST_TYPED_TEST_CASE_P_STATE_(CaseName), #CaseName, \
#CaseName, GTEST_REGISTERED_TEST_NAMES_(CaseName)) GTEST_REGISTERED_TEST_NAMES_(CaseName), \
::testing::internal::GenerateNames< \
::testing::internal::NameGeneratorSelector< \
__VA_ARGS__>::type, \
::testing::internal::TypeList< Types >::type>())
#endif // GTEST_HAS_TYPED_TEST_P #endif // GTEST_HAS_TYPED_TEST_P

View File

@ -606,6 +606,37 @@ inline std::string GetPrefixUntilComma(const char* str) {
void SplitString(const ::std::string& str, char delimiter, void SplitString(const ::std::string& str, char delimiter,
::std::vector< ::std::string>* dest); ::std::vector< ::std::string>* dest);
// The default argument to the template below for the case when the user does
// not provide a name generator.
struct DefaultNameGenerator {
template <typename T>
static std::string GetName(int i) {
return StreamableToString(i);
}
};
template <typename Provided = DefaultNameGenerator>
struct NameGeneratorSelector {
typedef Provided type;
};
template <typename NameGenerator>
void GenerateNamesRecursively(Types0, std::vector<std::string>*, int) {}
template <typename NameGenerator, typename Types>
void GenerateNamesRecursively(Types, std::vector<std::string>* result, int i) {
result->push_back(NameGenerator::template GetName<typename Types::Head>(i));
GenerateNamesRecursively<NameGenerator>(typename Types::Tail(), result,
i + 1);
}
template <typename NameGenerator, typename Types>
std::vector<std::string> GenerateNames() {
std::vector<std::string> result;
GenerateNamesRecursively<NameGenerator>(Types(), &result, 0);
return result;
}
// TypeParameterizedTest<Fixture, TestSel, Types>::Register() // TypeParameterizedTest<Fixture, TestSel, Types>::Register()
// registers a list of type-parameterized tests with Google Test. The // registers a list of type-parameterized tests with Google Test. The
// return value is insignificant - we just need to return something // return value is insignificant - we just need to return something
@ -620,10 +651,10 @@ class TypeParameterizedTest {
// specified in INSTANTIATE_TYPED_TEST_CASE_P(Prefix, TestCase, // specified in INSTANTIATE_TYPED_TEST_CASE_P(Prefix, TestCase,
// Types). Valid values for 'index' are [0, N - 1] where N is the // Types). Valid values for 'index' are [0, N - 1] where N is the
// length of Types. // length of Types.
static bool Register(const char* prefix, static bool Register(const char* prefix, const CodeLocation& code_location,
const CodeLocation& code_location, const char* case_name, const char* test_names, int index,
const char* case_name, const char* test_names, const std::vector<std::string>& type_names =
int index) { GenerateNames<DefaultNameGenerator, Types>()) {
typedef typename Types::Head Type; typedef typename Types::Head Type;
typedef Fixture<Type> FixtureClass; typedef Fixture<Type> FixtureClass;
typedef typename GTEST_BIND_(TestSel, Type) TestClass; typedef typename GTEST_BIND_(TestSel, Type) TestClass;
@ -631,20 +662,23 @@ class TypeParameterizedTest {
// First, registers the first type-parameterized test in the type // First, registers the first type-parameterized test in the type
// list. // list.
MakeAndRegisterTestInfo( MakeAndRegisterTestInfo(
(std::string(prefix) + (prefix[0] == '\0' ? "" : "/") + case_name + "/" (std::string(prefix) + (prefix[0] == '\0' ? "" : "/") + case_name +
+ StreamableToString(index)).c_str(), "/" + type_names[index])
.c_str(),
StripTrailingSpaces(GetPrefixUntilComma(test_names)).c_str(), StripTrailingSpaces(GetPrefixUntilComma(test_names)).c_str(),
GetTypeName<Type>().c_str(), GetTypeName<Type>().c_str(),
NULL, // No value parameter. NULL, // No value parameter.
code_location, code_location, GetTypeId<FixtureClass>(), TestClass::SetUpTestCase,
GetTypeId<FixtureClass>(), TestClass::TearDownTestCase, new TestFactoryImpl<TestClass>);
TestClass::SetUpTestCase,
TestClass::TearDownTestCase,
new TestFactoryImpl<TestClass>);
// Next, recurses (at compile time) with the tail of the type list. // Next, recurses (at compile time) with the tail of the type list.
return TypeParameterizedTest<Fixture, TestSel, typename Types::Tail> return TypeParameterizedTest<Fixture, TestSel,
::Register(prefix, code_location, case_name, test_names, index + 1); typename Types::Tail>::Register(prefix,
code_location,
case_name,
test_names,
index + 1,
type_names);
} }
}; };
@ -654,7 +688,9 @@ class TypeParameterizedTest<Fixture, TestSel, Types0> {
public: public:
static bool Register(const char* /*prefix*/, const CodeLocation&, static bool Register(const char* /*prefix*/, const CodeLocation&,
const char* /*case_name*/, const char* /*test_names*/, const char* /*case_name*/, const char* /*test_names*/,
int /*index*/) { int /*index*/,
const std::vector<std::string>& =
std::vector<std::string>() /*type_names*/) {
return true; return true;
} }
}; };
@ -667,8 +703,10 @@ template <GTEST_TEMPLATE_ Fixture, typename Tests, typename Types>
class TypeParameterizedTestCase { class TypeParameterizedTestCase {
public: public:
static bool Register(const char* prefix, CodeLocation code_location, static bool Register(const char* prefix, CodeLocation code_location,
const TypedTestCasePState* state, const TypedTestCasePState* state, const char* case_name,
const char* case_name, const char* test_names) { const char* test_names,
const std::vector<std::string>& type_names =
GenerateNames<DefaultNameGenerator, Types>()) {
std::string test_name = StripTrailingSpaces( std::string test_name = StripTrailingSpaces(
GetPrefixUntilComma(test_names)); GetPrefixUntilComma(test_names));
if (!state->TestExists(test_name)) { if (!state->TestExists(test_name)) {
@ -685,12 +723,14 @@ class TypeParameterizedTestCase {
// First, register the first test in 'Test' for each type in 'Types'. // First, register the first test in 'Test' for each type in 'Types'.
TypeParameterizedTest<Fixture, Head, Types>::Register( TypeParameterizedTest<Fixture, Head, Types>::Register(
prefix, test_location, case_name, test_names, 0); prefix, test_location, case_name, test_names, 0, type_names);
// Next, recurses (at compile time) with the tail of the test list. // Next, recurses (at compile time) with the tail of the test list.
return TypeParameterizedTestCase<Fixture, typename Tests::Tail, Types> return TypeParameterizedTestCase<Fixture, typename Tests::Tail,
::Register(prefix, code_location, state, Types>::Register(prefix, code_location,
case_name, SkipComma(test_names)); state, case_name,
SkipComma(test_names),
type_names);
} }
}; };
@ -700,7 +740,9 @@ class TypeParameterizedTestCase<Fixture, Templates0, Types> {
public: public:
static bool Register(const char* /*prefix*/, const CodeLocation&, static bool Register(const char* /*prefix*/, const CodeLocation&,
const TypedTestCasePState* /*state*/, const TypedTestCasePState* /*state*/,
const char* /*case_name*/, const char* /*test_names*/) { const char* /*case_name*/, const char* /*test_names*/,
const std::vector<std::string>& =
std::vector<std::string>() /*type_names*/) {
return true; return true;
} }
}; };

View File

@ -801,6 +801,28 @@ TYPED_TEST(TypedTest, Failure) {
EXPECT_EQ(1, TypeParam()) << "Expected failure"; EXPECT_EQ(1, TypeParam()) << "Expected failure";
} }
typedef testing::Types<char, int> TypesForTestWithNames;
template <typename T>
class TypedTestWithNames : public testing::Test {};
class TypedTestNames {
public:
template <typename T>
static std::string GetName(int i) {
if (testing::internal::IsSame<T, char>::value)
return std::string("char_") + ::testing::PrintToString(i);
if (testing::internal::IsSame<T, int>::value)
return std::string("int_") + ::testing::PrintToString(i);
}
};
TYPED_TEST_CASE(TypedTestWithNames, TypesForTestWithNames, TypedTestNames);
TYPED_TEST(TypedTestWithNames, Success) {}
TYPED_TEST(TypedTestWithNames, Failure) { FAIL(); }
#endif // GTEST_HAS_TYPED_TEST #endif // GTEST_HAS_TYPED_TEST
// This #ifdef block tests the output of type-parameterized tests. // This #ifdef block tests the output of type-parameterized tests.
@ -825,6 +847,22 @@ REGISTER_TYPED_TEST_CASE_P(TypedTestP, Success, Failure);
typedef testing::Types<unsigned char, unsigned int> UnsignedTypes; typedef testing::Types<unsigned char, unsigned int> UnsignedTypes;
INSTANTIATE_TYPED_TEST_CASE_P(Unsigned, TypedTestP, UnsignedTypes); INSTANTIATE_TYPED_TEST_CASE_P(Unsigned, TypedTestP, UnsignedTypes);
class TypedTestPNames {
public:
template <typename T>
static std::string GetName(int i) {
if (testing::internal::IsSame<T, unsigned char>::value) {
return std::string("unsigned_char_") + ::testing::PrintToString(i);
}
if (testing::internal::IsSame<T, unsigned int>::value) {
return std::string("unsigned_int_") + ::testing::PrintToString(i);
}
}
};
INSTANTIATE_TYPED_TEST_CASE_P(UnsignedCustomName, TypedTestP, UnsignedTypes,
TypedTestPNames);
#endif // GTEST_HAS_TYPED_TEST_P #endif // GTEST_HAS_TYPED_TEST_P
#if GTEST_HAS_DEATH_TEST #if GTEST_HAS_DEATH_TEST

View File

@ -165,6 +165,40 @@ TYPED_TEST(NumericTest, DefaultIsZero) {
} // namespace library1 } // namespace library1
// Tests that custom names work.
template <typename T>
class TypedTestWithNames : public Test {};
class TypedTestNames {
public:
template <typename T>
static std::string GetName(int i) {
if (testing::internal::IsSame<T, char>::value) {
return std::string("char_") + ::testing::PrintToString(i);
}
if (testing::internal::IsSame<T, int>::value) {
return std::string("int_") + ::testing::PrintToString(i);
}
}
};
TYPED_TEST_CASE(TypedTestWithNames, TwoTypes, TypedTestNames);
TYPED_TEST(TypedTestWithNames, TestCaseName) {
if (testing::internal::IsSame<TypeParam, char>::value) {
EXPECT_STREQ(::testing::UnitTest::GetInstance()
->current_test_info()
->test_case_name(),
"TypedTestWithNames/char_0");
}
if (testing::internal::IsSame<TypeParam, int>::value) {
EXPECT_STREQ(::testing::UnitTest::GetInstance()
->current_test_info()
->test_case_name(),
"TypedTestWithNames/int_1");
}
}
#endif // GTEST_HAS_TYPED_TEST #endif // GTEST_HAS_TYPED_TEST
// This #ifdef block tests type-parameterized tests. // This #ifdef block tests type-parameterized tests.
@ -265,6 +299,46 @@ REGISTER_TYPED_TEST_CASE_P(DerivedTest,
typedef Types<short, long> MyTwoTypes; typedef Types<short, long> MyTwoTypes;
INSTANTIATE_TYPED_TEST_CASE_P(My, DerivedTest, MyTwoTypes); INSTANTIATE_TYPED_TEST_CASE_P(My, DerivedTest, MyTwoTypes);
// Tests that custom names work with type parametrized tests. We reuse the
// TwoTypes from above here.
template <typename T>
class TypeParametrizedTestWithNames : public Test {};
TYPED_TEST_CASE_P(TypeParametrizedTestWithNames);
TYPED_TEST_P(TypeParametrizedTestWithNames, TestCaseName) {
if (testing::internal::IsSame<TypeParam, char>::value) {
EXPECT_STREQ(::testing::UnitTest::GetInstance()
->current_test_info()
->test_case_name(),
"CustomName/TypeParametrizedTestWithNames/p_char_0");
}
if (testing::internal::IsSame<TypeParam, int>::value) {
EXPECT_STREQ(::testing::UnitTest::GetInstance()
->current_test_info()
->test_case_name(),
"CustomName/TypeParametrizedTestWithNames/p_int_1");
}
}
REGISTER_TYPED_TEST_CASE_P(TypeParametrizedTestWithNames, TestCaseName);
class TypeParametrizedTestNames {
public:
template <typename T>
static std::string GetName(int i) {
if (testing::internal::IsSame<T, char>::value) {
return std::string("p_char_") + ::testing::PrintToString(i);
}
if (testing::internal::IsSame<T, int>::value) {
return std::string("p_int_") + ::testing::PrintToString(i);
}
}
};
INSTANTIATE_TYPED_TEST_CASE_P(CustomName, TypeParametrizedTestWithNames,
TwoTypes, TypeParametrizedTestNames);
// Tests that multiple TYPED_TEST_CASE_P's can be defined in the same // Tests that multiple TYPED_TEST_CASE_P's can be defined in the same
// translation unit. // translation unit.