From a2b1a8556ea64014606d78b09333d9c522430a25 Mon Sep 17 00:00:00 2001 From: shiqian Date: Mon, 8 Sep 2008 17:55:52 +0000 Subject: Adds support for type-parameterized tests (by Zhanyong Wan); also adds case-insensitive wide string comparison to the String class (by Vlad Losev). --- include/gtest/gtest.h | 83 ++++++------- include/gtest/internal/gtest-internal.h | 207 ++++++++++++++++++++++++++++++-- include/gtest/internal/gtest-port.h | 14 ++- include/gtest/internal/gtest-string.h | 19 +++ 4 files changed, 266 insertions(+), 57 deletions(-) (limited to 'include') diff --git a/include/gtest/gtest.h b/include/gtest/gtest.h index 1f5d7640..37ea6b04 100644 --- a/include/gtest/gtest.h +++ b/include/gtest/gtest.h @@ -62,11 +62,13 @@ // Windows proper with Visual C++ and MS C library (_MSC_VER && !_WIN32_WCE) and // Windows Mobile with Visual C++ and no C library (_WIN32_WCE). +#include #include #include #include #include #include +#include // Depending on the platform, different string classes are available. // On Windows, ::std::string compiles only when exceptions are @@ -217,12 +219,28 @@ class Test { // Defines types for pointers to functions that set up and tear down // a test case. - typedef void (*SetUpTestCaseFunc)(); - typedef void (*TearDownTestCaseFunc)(); + typedef internal::SetUpTestCaseFunc SetUpTestCaseFunc; + typedef internal::TearDownTestCaseFunc TearDownTestCaseFunc; // The d'tor is virtual as we intend to inherit from Test. virtual ~Test(); + // Sets up the stuff shared by all tests in this test case. + // + // Google Test will call Foo::SetUpTestCase() before running the first + // test in test case Foo. Hence a sub-class can define its own + // SetUpTestCase() method to shadow the one defined in the super + // class. + static void SetUpTestCase() {} + + // Tears down the stuff shared by all tests in this test case. + // + // Google Test will call Foo::TearDownTestCase() after running the last + // test in test case Foo. Hence a sub-class can define its own + // TearDownTestCase() method to shadow the one defined in the super + // class. + static void TearDownTestCase() {} + // Returns true iff the current test has a fatal failure. static bool HasFatalFailure(); @@ -245,22 +263,6 @@ class Test { // Creates a Test object. Test(); - // Sets up the stuff shared by all tests in this test case. - // - // Google Test will call Foo::SetUpTestCase() before running the first - // test in test case Foo. Hence a sub-class can define its own - // SetUpTestCase() method to shadow the one defined in the super - // class. - static void SetUpTestCase() {} - - // Tears down the stuff shared by all tests in this test case. - // - // Google Test will call Foo::TearDownTestCase() after running the last - // test in test case Foo. Hence a sub-class can define its own - // TearDownTestCase() method to shadow the one defined in the super - // class. - static void TearDownTestCase() {} - // Sets up the test fixture. virtual void SetUp(); @@ -327,36 +329,18 @@ class TestInfo { // don't inherit from TestInfo. ~TestInfo(); - // Creates a TestInfo object and registers it with the UnitTest - // singleton; returns the created object. - // - // Arguments: - // - // test_case_name: name of the test case - // name: name of the test - // fixture_class_id: ID of the test fixture class - // set_up_tc: pointer to the function that sets up the test case - // tear_down_tc: pointer to the function that tears down the test case - // factory: Pointer to the factory that creates a test object. - // The newly created TestInfo instance will assume - // ownershi pof the factory object. - // - // This is public only because it's needed by the TEST and TEST_F macros. - // INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. - static TestInfo* MakeAndRegisterInstance( - const char* test_case_name, - const char* name, - internal::TypeId fixture_class_id, - Test::SetUpTestCaseFunc set_up_tc, - Test::TearDownTestCaseFunc tear_down_tc, - internal::TestFactoryBase* factory); - // Returns the test case name. const char* test_case_name() const; // Returns the test name. const char* name() const; + // Returns the test case comment. + const char* test_case_comment() const; + + // Returns the test comment. + const char* comment() const; + // Returns true if this test should run. // // Google Test allows the user to filter the tests by their full names. @@ -383,6 +367,13 @@ class TestInfo { friend class internal::UnitTestImpl; friend class Test; friend class TestCase; + friend TestInfo* internal::MakeAndRegisterTestInfo( + const char* test_case_name, const char* name, + const char* test_case_comment, const char* comment, + internal::TypeId fixture_class_id, + Test::SetUpTestCaseFunc set_up_tc, + Test::TearDownTestCaseFunc tear_down_tc, + internal::TestFactoryBase* factory); // Increments the number of death tests encountered in this test so // far. @@ -395,6 +386,7 @@ class TestInfo { // Constructs a TestInfo object. The newly constructed instance assumes // ownership of the factory object. TestInfo(const char* test_case_name, const char* name, + const char* test_case_comment, const char* comment, internal::TypeId fixture_class_id, internal::TestFactoryBase* factory); @@ -1118,9 +1110,10 @@ AssertionResult DoubleLE(const char* expr1, const char* expr2, // // * {ASSERT|EXPECT}_HRESULT_{SUCCEEDED|FAILED}(expr) // -// When expr unexpectedly fails or succeeds, Google Test prints the expected result -// and the actual result with both a human-readable string representation of -// the error, if available, as well as the hex result code. +// When expr unexpectedly fails or succeeds, Google Test prints the +// expected result and the actual result with both a human-readable +// string representation of the error, if available, as well as the +// hex result code. #define EXPECT_HRESULT_SUCCEEDED(expr) \ EXPECT_PRED_FORMAT1(::testing::internal::IsHRESULTSuccess, (expr)) diff --git a/include/gtest/internal/gtest-internal.h b/include/gtest/internal/gtest-internal.h index dc6154b6..8adf13e4 100644 --- a/include/gtest/internal/gtest-internal.h +++ b/include/gtest/internal/gtest-internal.h @@ -46,11 +46,15 @@ #include #endif // GTEST_OS_LINUX -#include // NOLINT -#include // NOLINT +#include +#include +#include +#include +#include #include #include +#include // Due to C++ preprocessor weirdness, we need double indirection to // concatenate two tokens when one of them is __LINE__. Writing @@ -521,6 +525,182 @@ AssertionResult IsHRESULTFailure(const char* expr, long hr); // NOLINT #endif // GTEST_OS_WINDOWS +// Formats a source file path and a line number as they would appear +// in a compiler error message. +inline String FormatFileLocation(const char* file, int line) { + const char* const file_name = file == NULL ? "unknown file" : file; + if (line < 0) { + return String::Format("%s:", file_name); + } +#ifdef _MSC_VER + return String::Format("%s(%d):", file_name, line); +#else + return String::Format("%s:%d:", file_name, line); +#endif // _MSC_VER +} + +// Types of SetUpTestCase() and TearDownTestCase() functions. +typedef void (*SetUpTestCaseFunc)(); +typedef void (*TearDownTestCaseFunc)(); + +// Creates a new TestInfo object and registers it with Google Test; +// returns the created object. +// +// Arguments: +// +// test_case_name: name of the test case +// name: name of the test +// test_case_comment: a comment on the test case that will be included in +// the test output +// comment: a comment on the test that will be included in the +// test output +// fixture_class_id: ID of the test fixture class +// set_up_tc: pointer to the function that sets up the test case +// tear_down_tc: pointer to the function that tears down the test case +// factory: pointer to the factory that creates a test object. +// The newly created TestInfo instance will assume +// ownership of the factory object. +TestInfo* MakeAndRegisterTestInfo( + const char* test_case_name, const char* name, + const char* test_case_comment, const char* comment, + TypeId fixture_class_id, + SetUpTestCaseFunc set_up_tc, + TearDownTestCaseFunc tear_down_tc, + TestFactoryBase* factory); + +#if defined(GTEST_HAS_TYPED_TEST) || defined(GTEST_HAS_TYPED_TEST_P) + +// State of the definition of a type-parameterized test case. +class TypedTestCasePState { + public: + TypedTestCasePState() : registered_(false) {} + + // Adds the given test name to defined_test_names_ and return true + // if the test case hasn't been registered; otherwise aborts the + // program. + bool AddTestName(const char* file, int line, const char* case_name, + const char* test_name) { + if (registered_) { + fprintf(stderr, "%s Test %s must be defined before " + "REGISTER_TYPED_TEST_CASE_P(%s, ...).\n", + FormatFileLocation(file, line).c_str(), test_name, case_name); + abort(); + } + defined_test_names_.insert(test_name); + return true; + } + + // Verifies that registered_tests match the test names in + // defined_test_names_; returns registered_tests if successful, or + // aborts the program otherwise. + const char* VerifyRegisteredTestNames( + const char* file, int line, const char* registered_tests); + + private: + bool registered_; + ::std::set defined_test_names_; +}; + +// Skips to the first non-space char after the first comma in 'str'; +// returns NULL if no comma is found in 'str'. +inline const char* SkipComma(const char* str) { + const char* comma = strchr(str, ','); + if (comma == NULL) { + return NULL; + } + while (isspace(*(++comma))) {} + return comma; +} + +// Returns the prefix of 'str' before the first comma in it; returns +// the entire string if it contains no comma. +inline String GetPrefixUntilComma(const char* str) { + const char* comma = strchr(str, ','); + return comma == NULL ? String(str) : String(str, comma - str); +} + +// TypeParameterizedTest::Register() +// registers a list of type-parameterized tests with Google Test. The +// return value is insignificant - we just need to return something +// such that we can call this function in a namespace scope. +// +// Implementation note: The GTEST_TEMPLATE_ macro declares a template +// template parameter. It's defined in gtest-type-util.h. +template +class TypeParameterizedTest { + public: + // 'index' is the index of the test in the type list 'Types' + // specified in INSTANTIATE_TYPED_TEST_CASE_P(Prefix, TestCase, + // Types). Valid values for 'index' are [0, N - 1] where N is the + // length of Types. + static bool Register(const char* prefix, const char* case_name, + const char* test_names, int index) { + typedef typename Types::Head Type; + typedef Fixture FixtureClass; + typedef typename GTEST_BIND_(TestSel, Type) TestClass; + + // First, registers the first type-parameterized test in the type + // list. + MakeAndRegisterTestInfo( + String::Format("%s%s%s/%d", prefix, prefix[0] == '\0' ? "" : "/", + case_name, index).c_str(), + GetPrefixUntilComma(test_names).c_str(), + String::Format("TypeParam = %s", GetTypeName().c_str()).c_str(), + "", + GetTypeId(), + TestClass::SetUpTestCase, + TestClass::TearDownTestCase, + new TestFactoryImpl); + + // Next, recurses (at compile time) with the tail of the type list. + return TypeParameterizedTest + ::Register(prefix, case_name, test_names, index + 1); + } +}; + +// The base case for the compile time recursion. +template +class TypeParameterizedTest { + public: + static bool Register(const char* /*prefix*/, const char* /*case_name*/, + const char* /*test_names*/, int /*index*/) { + return true; + } +}; + +// TypeParameterizedTestCase::Register() +// registers *all combinations* of 'Tests' and 'Types' with Google +// Test. The return value is insignificant - we just need to return +// something such that we can call this function in a namespace scope. +template +class TypeParameterizedTestCase { + public: + static bool Register(const char* prefix, const char* case_name, + const char* test_names) { + typedef typename Tests::Head Head; + + // First, register the first test in 'Test' for each type in 'Types'. + TypeParameterizedTest::Register( + prefix, case_name, test_names, 0); + + // Next, recurses (at compile time) with the tail of the test list. + return TypeParameterizedTestCase + ::Register(prefix, case_name, SkipComma(test_names)); + } +}; + +// The base case for the compile time recursion. +template +class TypeParameterizedTestCase { + public: + static bool Register(const char* prefix, const char* case_name, + const char* test_names) { + return true; + } +}; + +#endif // GTEST_HAS_TYPED_TEST || GTEST_HAS_TYPED_TEST_P + } // namespace internal } // namespace testing @@ -544,27 +724,32 @@ AssertionResult IsHRESULTFailure(const char* expr, long hr); // NOLINT else \ fail("Value of: " booltext "\n Actual: " #actual "\nExpected: " #expected) +// Expands to the name of the class that implements the given test. +#define GTEST_TEST_CLASS_NAME_(test_case_name, test_name) \ + test_case_name##_##test_name##_Test + // Helper macro for defining tests. #define GTEST_TEST(test_case_name, test_name, parent_class)\ -class test_case_name##_##test_name##_Test : public parent_class {\ +class GTEST_TEST_CLASS_NAME_(test_case_name, test_name) : public parent_class {\ public:\ - test_case_name##_##test_name##_Test() {}\ + GTEST_TEST_CLASS_NAME_(test_case_name, test_name)() {}\ private:\ virtual void TestBody();\ static ::testing::TestInfo* const test_info_;\ - GTEST_DISALLOW_COPY_AND_ASSIGN(test_case_name##_##test_name##_Test);\ + GTEST_DISALLOW_COPY_AND_ASSIGN(\ + GTEST_TEST_CLASS_NAME_(test_case_name, test_name));\ };\ \ -::testing::TestInfo* const test_case_name##_##test_name##_Test::test_info_ =\ - ::testing::TestInfo::MakeAndRegisterInstance(\ - #test_case_name, \ - #test_name, \ +::testing::TestInfo* const GTEST_TEST_CLASS_NAME_(test_case_name, test_name)\ + ::test_info_ =\ + ::testing::internal::MakeAndRegisterTestInfo(\ + #test_case_name, #test_name, "", "", \ ::testing::internal::GetTypeId< parent_class >(), \ parent_class::SetUpTestCase, \ parent_class::TearDownTestCase, \ new ::testing::internal::TestFactoryImpl<\ - test_case_name##_##test_name##_Test>);\ -void test_case_name##_##test_name##_Test::TestBody() + GTEST_TEST_CLASS_NAME_(test_case_name, test_name)>);\ +void GTEST_TEST_CLASS_NAME_(test_case_name, test_name)::TestBody() #endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_INTERNAL_H_ diff --git a/include/gtest/internal/gtest-port.h b/include/gtest/internal/gtest-port.h index 5be0d539..75429b2b 100644 --- a/include/gtest/internal/gtest-port.h +++ b/include/gtest/internal/gtest-port.h @@ -73,7 +73,10 @@ // Note that it is possible that none of the GTEST_OS_ macros are defined. // // Macros indicating available Google Test features: -// GTEST_HAS_DEATH_TEST - defined iff death tests are supported. +// GTEST_HAS_DEATH_TEST - defined iff death tests are supported. +// GTEST_HAS_TYPED_TEST - defined iff typed tests are supported. +// GTEST_HAS_TYPED_TEST_P - defined iff type-parameterized tests are +// supported. // // Macros for basic C++ coding: // GTEST_AMBIGUOUS_ELSE_BLOCKER - for disabling a gcc warning. @@ -225,6 +228,15 @@ #include #endif // GTEST_HAS_STD_STRING && defined(GTEST_OS_LINUX) +// Determines whether to support type-driven tests. + +// Typed tests need and variadic macros, which gcc and VC +// 8.0+ support. +#if defined(__GNUC__) || (_MSC_VER >= 1400) +#define GTEST_HAS_TYPED_TEST +#define GTEST_HAS_TYPED_TEST_P +#endif // defined(__GNUC__) || (_MSC_VER >= 1400) + // Determines whether the system compiler uses UTF-16 for encoding wide strings. #if defined(GTEST_OS_WINDOWS) || defined(GTEST_OS_CYGWIN) || \ defined(__SYMBIAN32__) diff --git a/include/gtest/internal/gtest-string.h b/include/gtest/internal/gtest-string.h index 612b6cef..b37ff4f4 100644 --- a/include/gtest/internal/gtest-string.h +++ b/include/gtest/internal/gtest-string.h @@ -167,6 +167,21 @@ class String { static bool CaseInsensitiveCStringEquals(const char* lhs, const char* rhs); + // Compares two wide C strings, ignoring case. Returns true iff they + // have the same content. + // + // Unlike wcscasecmp(), this function can handle NULL argument(s). + // A NULL C string is considered different to any non-NULL wide C string, + // including the empty string. + // NB: The implementations on different platforms slightly differ. + // On windows, this method uses _wcsicmp which compares according to LC_CTYPE + // environment variable. On GNU platform this method uses wcscasecmp + // which compares according to LC_CTYPE category of the current locale. + // On MacOS X, it uses towlower, which also uses LC_CTYPE category of the + // current locale. + static bool CaseInsensitiveWideCStringEquals(const wchar_t* lhs, + const wchar_t* rhs); + // Formats a list of arguments to a String, using the same format // spec string as for printf. // @@ -218,6 +233,10 @@ class String { return CStringEquals(c_str_, c_str); } + // Returns true iff this String is less than the given C string. A NULL + // string is considered less than "". + bool operator<(const String& rhs) const { return Compare(rhs) < 0; } + // Returns true iff this String doesn't equal the given C string. A NULL // string and a non-NULL string are considered not equal. bool operator!=(const char* c_str) const { -- cgit v1.2.3