From deeaeb942e20b6711c01e58653f50934d51b5246 Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Tue, 10 Dec 2019 22:43:25 -0500 Subject: Export Test - Do Not Merge Detect when C++ parametric tests (TEST_P) are not instantiated. When an un-instantiated TEST_P is found, a new test will be inserted that will emit a warning message. This can be made to error with minor code edits. In the future, that is intended to be the default. PiperOrigin-RevId: 284901666 --- .../include/gtest/internal/gtest-param-util.h | 15 +++++- googletest/src/gtest.cc | 60 ++++++++++++++++++++++ .../test/googletest-output-test-golden-lin.txt | 12 +++-- googletest/test/googletest-output-test_.cc | 7 +++ googletest/test/googletest-param-test-test.cc | 6 +++ 5 files changed, 96 insertions(+), 4 deletions(-) diff --git a/googletest/include/gtest/internal/gtest-param-util.h b/googletest/include/gtest/internal/gtest-param-util.h index e900b3f..539fa52 100644 --- a/googletest/include/gtest/internal/gtest-param-util.h +++ b/googletest/include/gtest/internal/gtest-param-util.h @@ -42,12 +42,14 @@ #include #include #include +#include #include #include #include "gtest/internal/gtest-internal.h" #include "gtest/internal/gtest-port.h" #include "gtest/gtest-printers.h" +#include "gtest/gtest-test-part.h" namespace testing { // Input to a parameterized test name generator, describing a test parameter. @@ -472,6 +474,8 @@ class ParameterizedTestSuiteInfoBase { GTEST_DISALLOW_COPY_AND_ASSIGN_(ParameterizedTestSuiteInfoBase); }; +void InsertSyntheticTestCase(const std::string &name, CodeLocation location); + // INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. // // ParameterizedTestSuiteInfo accumulates tests obtained from TEST_P @@ -522,11 +526,13 @@ class ParameterizedTestSuiteInfo : public ParameterizedTestSuiteInfoBase { return 0; // Return value used only to run this method in namespace scope. } // UnitTest class invokes this method to register tests in this test suite - // test suites right before running tests in RUN_ALL_TESTS macro. + // right before running tests in RUN_ALL_TESTS macro. // This method should not be called more than once on any single // instance of a ParameterizedTestSuiteInfoBase derived class. // UnitTest has a guard to prevent from calling this method more than once. void RegisterTests() override { + bool generated_instantiations = false; + for (typename TestInfoContainer::iterator test_it = tests_.begin(); test_it != tests_.end(); ++test_it) { std::shared_ptr test_info = *test_it; @@ -549,6 +555,8 @@ class ParameterizedTestSuiteInfo : public ParameterizedTestSuiteInfoBase { for (typename ParamGenerator::iterator param_it = generator.begin(); param_it != generator.end(); ++param_it, ++i) { + generated_instantiations = true; + Message test_name_stream; std::string param_name = name_func( @@ -577,6 +585,11 @@ class ParameterizedTestSuiteInfo : public ParameterizedTestSuiteInfoBase { } // for param_it } // for gen_it } // for test_it + + if (!generated_instantiations) { + // There are no generaotrs, or they all generate nothing ... + InsertSyntheticTestCase(GetTestSuiteName(), code_location_); + } } // RegisterTests private: diff --git a/googletest/src/gtest.cc b/googletest/src/gtest.cc index 3dbf804..f6466b9 100644 --- a/googletest/src/gtest.cc +++ b/googletest/src/gtest.cc @@ -407,6 +407,66 @@ void AssertHelper::operator=(const Message& message) const { ); // NOLINT } +namespace { + +// When TEST_P is found without a matching INSTANTIATE_TEST_SUITE_P +// to creates test cases for it, a syntetic test case is +// inserted to report ether an error or a log message. +// +// This configuration bit will likely be removed at some point. +constexpr bool kErrorOnUninstantiatedParameterizedTest = false; + +// A test that fails at a given file/line location with a given message. +class FailureTest : public Test { + public: + explicit FailureTest(const CodeLocation& loc, std::string error_message, + bool as_error) + : loc_(loc), + error_message_(std::move(error_message)), + as_error_(as_error) {} + + void TestBody() override { + if (as_error_) { + AssertHelper(TestPartResult::kNonFatalFailure, loc_.file.c_str(), + loc_.line, "") = Message() << error_message_; + } else { + std::cout << error_message_ << std::endl; + } + } + + private: + const CodeLocation loc_; + const std::string error_message_; + const bool as_error_; +}; + + +} // namespace + +// If this parameterized test suite has no instantiations (and that +// has not been marked as okay), emit a test case reporting that. +void InsertSyntheticTestCase(const std::string &name, CodeLocation location) { + std::string message = + "Paramaterized test suite " + name + + " is defined via TEST_P, but never instantiated. None of the test cases " + "will run. Either no INSTANTIATE_TEST_SUITE_P is provided or the only " + "ones provided expand to nothing." + "\n\n" + "Ideally, TEST_P definitions should only ever be included as part of " + "binaries that intend to use them. (As opposed to, for example, being " + "placed in a library that may be linked in to get other utilities.)"; + + std::string full_name = "UninstantiatedParamaterizedTestSuite<" + name + ">"; + RegisterTest( // + "GoogleTestVerification", full_name.c_str(), + nullptr, // No type parameter. + nullptr, // No value parameter. + location.file.c_str(), location.line, [message, location] { + return new FailureTest(location, message, + kErrorOnUninstantiatedParameterizedTest); + }); +} + // A copy of all command line arguments. Set by InitGoogleTest(). static ::std::vector g_argvs; diff --git a/googletest/test/googletest-output-test-golden-lin.txt b/googletest/test/googletest-output-test-golden-lin.txt index 270b15a..27f9d48 100644 --- a/googletest/test/googletest-output-test-golden-lin.txt +++ b/googletest/test/googletest-output-test-golden-lin.txt @@ -12,7 +12,7 @@ Expected equality of these values: 3 Stack trace: (omitted) -[==========] Running 84 tests from 39 test suites. +[==========] Running 85 tests from 40 test suites. [----------] Global test environment set-up. FooEnvironment::SetUp() called. BarEnvironment::SetUp() called. @@ -979,6 +979,12 @@ Expected failure Stack trace: (omitted) [ FAILED ] PrintingStrings/ParamTest.Failure/a, where GetParam() = "a" +[----------] 1 test from GoogleTestVerification +[ RUN ] GoogleTestVerification.UninstantiatedParamaterizedTestSuite +Paramaterized test suite DetectNotInstantiatedTest is defined via TEST_P, but never instantiated. None of the test cases will run. Either no INSTANTIATE_TEST_SUITE_P is provided or the only ones provided expand to nothing. + +Ideally, TEST_P definitions should only ever be included as part of binaries that intend to use them. (As opposed to, for example, being placed in a library that may be linked in to get other utilities.) +[ OK ] GoogleTestVerification.UninstantiatedParamaterizedTestSuite [----------] Global test environment tear-down BarEnvironment::TearDown() called. googletest-output-test_.cc:#: Failure @@ -992,8 +998,8 @@ Failed Expected fatal failure. Stack trace: (omitted) -[==========] 84 tests from 39 test suites ran. -[ PASSED ] 30 tests. +[==========] 85 tests from 40 test suites ran. +[ PASSED ] 31 tests. [ FAILED ] 54 tests, listed below: [ FAILED ] NonfatalFailureTest.EscapesStringOperands [ FAILED ] NonfatalFailureTest.DiffForLongStrings diff --git a/googletest/test/googletest-output-test_.cc b/googletest/test/googletest-output-test_.cc index f724cca..fe0d83f 100644 --- a/googletest/test/googletest-output-test_.cc +++ b/googletest/test/googletest-output-test_.cc @@ -782,6 +782,13 @@ INSTANTIATE_TEST_SUITE_P(PrintingStrings, testing::Values(std::string("a")), ParamNameFunc); +// fails under kErrorOnUninstantiatedParameterizedTest=true +class DetectNotInstantiatedTest : public testing::TestWithParam {}; +TEST_P(DetectNotInstantiatedTest, Used) { } + +// This would make the test failure from the above go away. +// INSTANTIATE_TEST_SUITE_P(Fix, DetectNotInstantiatedTest, testing::Values(1)); + // This #ifdef block tests the output of typed tests. #if GTEST_HAS_TYPED_TEST diff --git a/googletest/test/googletest-param-test-test.cc b/googletest/test/googletest-param-test-test.cc index 2740aaa..85c76c3 100644 --- a/googletest/test/googletest-param-test-test.cc +++ b/googletest/test/googletest-param-test-test.cc @@ -1068,6 +1068,12 @@ TEST_P(MyEnumTest, ChecksParamMoreThanZero) { EXPECT_GE(10, GetParam()); } INSTANTIATE_TEST_SUITE_P(MyEnumTests, MyEnumTest, ::testing::Values(ENUM1, ENUM2, 0)); +namespace works_here { +// Never used not instantiated, this should work. +class NotUsedTest : public testing::TestWithParam {}; + +} // namespace works_here + int main(int argc, char **argv) { // Used in TestGenerationTest test suite. AddGlobalTestEnvironment(TestGenerationTest::Environment::Instance()); -- cgit v0.12