probinson created this revision. probinson added reviewers: MaskRay, dblaikie. Herald added a subscriber: mgrang. probinson requested review of this revision. Herald added projects: clang, LLVM. Herald added a subscriber: cfe-commits.
This is an enhancement of the 'googletest' support code, to report test assertions (EXPECT_* and ASSERT_* macros) that are not executed. An un-executed assertion looks like it tests something, and passes (so is "green") but actually does nothing (so is "rotten"). Due to the number of false positives (for example, all DISABLED tests will be reported as rotten), running the unittests under lit will *not* report rotten tests; however, running a unittest program manually (not under lit) will report these by default. The RGT support has been tried on Linux using both Clang and GCC as build compilers, and on Windows with MSVC. MachO has not been tried. The intent is to clean up more of the false positives before seeking to commit the RGT support. Some of this work has been done already. This is a Work-In-Progress, not ready for final review, but it seems mature enough to present to the community in its current form. Repository: rG LLVM Github Monorepo https://reviews.llvm.org/D97566 Files: clang/test/Unit/lit.cfg.py clang/unittests/ASTMatchers/ASTMatchersInternalTest.cpp clang/unittests/Tooling/Syntax/MutationsTest.cpp llvm/test/Unit/lit.cfg.py llvm/unittests/Support/ProgramTest.cpp llvm/utils/unittest/googletest/include/gtest/gtest.h llvm/utils/unittest/googletest/include/gtest/gtest_pred_impl.h llvm/utils/unittest/googletest/include/gtest/internal/gtest-internal.h llvm/utils/unittest/googletest/include/gtest/internal/rgt.h llvm/utils/unittest/googletest/src/gtest-all.cc llvm/utils/unittest/googletest/src/gtest-internal-inl.h llvm/utils/unittest/googletest/src/gtest.cc llvm/utils/unittest/googletest/src/rgt.cc
Index: llvm/utils/unittest/googletest/src/rgt.cc =================================================================== --- /dev/null +++ llvm/utils/unittest/googletest/src/rgt.cc @@ -0,0 +1,236 @@ +//===- rgt.cc - Rotten Green Test support ---------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Support for the Rotten Green Test extension to LLVM's copy of the +// googletest framework. + +#include "gtest/internal/rgt.h" +#include <algorithm> +#include <cassert> +#include <map> +#include <vector> + +// When we can't get pure static initialization of the array of items +// describing test points, they get registered on startup and we keep +// pointers to the data in this vector. + +#if GTEST_RGT_RUNTIME_INIT_ + +namespace { + +using item_vector = std::vector<testing::internal::RGT_item *>; +class RegisteredItems { + static item_vector *items; +public: + RegisteredItems() = default; + item_vector *getItems() { + if (!items) + items = new item_vector; + return items; + } + int size() { return getItems()->size(); } + bool empty() { return getItems()->empty(); } + auto begin() { return getItems()->begin(); } + auto end() { return getItems()->end(); } + void push_back(testing::internal::RGT_item *item) { + getItems()->push_back(item); + } +}; + +RegisteredItems registered_items; +item_vector *RegisteredItems::items = nullptr; + +} // end anonymous namespace + +void testing::internal::RGT_record(testing::internal::RGT_item *item) { + registered_items.push_back(item); +} + +#if GTEST_RGT_RUNTIME_INIT_MANUAL_ +// On Windows we have to allocate our own placeholder start/stop data items. +// The linker will sort these into the right order relative to real data. +// Because the section concatenation might be padded, we'll have to skip over +// any items with a null file pointer, so make the first item look like it's +// padding. + +#define START_SECTION_NAME GTEST_RGT_SECTION_NAME_WITH_SUFFIX_($a) +#define STOP_SECTION_NAME GTEST_RGT_SECTION_NAME_WITH_SUFFIX_($z) + +#pragma section (START_SECTION_NAME,read,write) +__declspec(allocate(START_SECTION_NAME)) +RGT_recorder RGT_manual_init_start = (RGT_recorder)1; + +#pragma section (STOP_SECTION_NAME,read,write) +__declspec(allocate(STOP_SECTION_NAME)) +RGT_recorder RGT_manual_init_stop = (RGT_recorder)1; + +void testing::internal::RGT_init_manual() { + const RGT_recorder *F = &RGT_manual_init_start; + int call_count = 0; + int null_count = 0; + for (++F; F < &RGT_manual_init_stop; ++F) { + if (*F) { + ++call_count; + (*F)(); + } else { + ++null_count; + } + } + printf("RGT_init_manual: %d called, %d null\n", call_count, null_count); +} + +#endif // GTEST_RGT_RUNTIME_INIT_MANUAL_ +#endif // GTEST_RGT_RUNTIME_INIT_ + +// In order to deduplicate information due to template instantiations, +// we combine all raw reports for the same source location. Then for +// nicer reporting, we generate a vector such that all reports for a +// given file are sorted by line number. +// +// The runtime initialization tactic can result in the same item being +// registered multiple times; the deduplication automatically handles +// that as well. + +namespace testing { +namespace internal { + +bool operator<(const RGT_item &a, const RGT_item &b) { + assert(a.file == b.file); + return a.line < b.line; +} + +} // end namespace internal +} // end namespace testing + +#if !GTEST_RGT_RUNTIME_INIT_ +// Non-Windows linkers provide __start_<section> and __stop_<section> symbols. +// Declare these outside of all namespaces. +#define GEN_NAME2(prefix, section) prefix ## section +#define GEN_NAME(prefix, section) GEN_NAME2(prefix, section) +#define START_NAME GEN_NAME(__start_, GTEST_RGT_SECTION_NAME_) +#define STOP_NAME GEN_NAME(__stop_, GTEST_RGT_SECTION_NAME_) + +// extern "C" vars can't have qualified type names; we only care about +// the addresses, we'll do appropriate casts later. +extern "C" int START_NAME; +extern "C" int STOP_NAME; +#endif // !GTEST_RGT_RUNTIME_INIT_ + +namespace { + +std::vector<testing::internal::RGT_item> RGT_collated_data; + +void collate_data() { + if (!RGT_collated_data.empty()) + return; + + // Collect raw data into a map with filename as the key; the value is + // another map with line number as the key and "executed" as the value. + // This collection deduplicates entries, and markes a line as executed + // if any of the (possibly duplicate) entries was marked executed. + // We sort the data by filename, because we run in various environments + // and sorting the output makes the data easier to compare. + struct test_info_compare { + bool operator()(const char *lhs, const char *rhs) const { + return strcmp(lhs, rhs) < 0; + } + }; + using file_info = std::map<unsigned, bool>; + using test_info = std::map<const char*, file_info, test_info_compare>; + + test_info test_map; + testing::internal::RGT_item *item; +#if GTEST_RGT_RUNTIME_INIT_ + item_vector::iterator I = registered_items.begin(); + item_vector::iterator E = registered_items.end(); +#define GET_ITEM item = *I +#else + testing::internal::RGT_item *I = (testing::internal::RGT_item *)&START_NAME; + testing::internal::RGT_item *E = (testing::internal::RGT_item *)&STOP_NAME; +#define GET_ITEM item = I +#endif + + // This assumes all pointers to the same filename string are equal. + for (; I != E; ++I) { + GET_ITEM; + file_info &fileinfo = test_map[item->file]; + bool &was_executed = fileinfo[item->line]; + if (item->executed) + was_executed = true; + } + + + // Serialize the collated data into a vector where the data for a given + // file is all in adjacent entries, and sorted by line. + for (auto &M : test_map) { + const char *file = M.first; + // As we're adding elements to the vector, we can't preserve iterators. + std::size_t file_start = RGT_collated_data.size(); + for (auto &I : M.second) + RGT_collated_data.push_back({file, I.first, I.second}); + std::sort(RGT_collated_data.begin() + file_start, RGT_collated_data.end()); + } +} + +} // end anonymous namespace + +int testing::internal::RGT_get_item_count() { + collate_data(); + return RGT_collated_data.size(); +} + +// Iterator-like function to find un-executed test points. + +testing::internal::RGT_item * +testing::internal::RGT_get_next_rotten_item(testing::internal::RGT_item *item) { + collate_data(); + + if (item) + ++item; + else + item = &*RGT_collated_data.begin(); + for (; item != &*RGT_collated_data.end(); ++item) { + if (!item->executed) + return item; + } + + return nullptr; +} + +// Report source location of all identified assertions. For debugging. +// We emit one per line to simplify diffing. + +void testing::internal::RGT_report_all_assertions_to(std::string &filename) { + if (filename.empty()) + return; + + // Following is based on UnitTestOptions::GetAbsolutePathToOutputFile() + // which has a note regarding certain Windows paths not working. + internal::FilePath log_path(filename); + if (!log_path.IsAbsolutePath()) { + log_path = internal::FilePath::ConcatPaths( + internal::FilePath(UnitTest::GetInstance()->original_working_dir()), + internal::FilePath(filename)); + } + + if (log_path.IsDirectory()) { + fprintf(stderr, "Specified log file is a directory \"%s\"\n", + filename.c_str()); + return; + } + FILE* logfile = posix::FOpen(log_path.c_str(), "w"); + if (!logfile) { + fprintf(stderr, "Unable to open log file \"%s\"\n", log_path.c_str()); + return; + } + + collate_data(); + for (auto &item : RGT_collated_data) + fprintf(logfile, "%s::%u\n", item.file, item.line); + posix::FClose(logfile); +} Index: llvm/utils/unittest/googletest/src/gtest.cc =================================================================== --- llvm/utils/unittest/googletest/src/gtest.cc +++ llvm/utils/unittest/googletest/src/gtest.cc @@ -303,6 +303,18 @@ "This flag specifies the flagfile to read command-line flags from."); #endif // GTEST_USE_OWN_FLAGFILE_FLAG_ +GTEST_DEFINE_bool_( + report_rotten, + internal::BoolFromGTestEnv("report_rotten", true), + "When this flag is specified, the source locations of un-executed " + "test assertions will be reported."); + +GTEST_DEFINE_string_( + report_all_assertions_to, + internal::StringFromGTestEnv("report_all_assertions_to", ""), + "This flag specifies the file on which to report the source locations " + "of all test assertions."); + namespace internal { // Generates a random number from [0, range), using a Linear @@ -3230,6 +3242,63 @@ // End PrettyUnitTestResultPrinter +// This class implements the TestEventListener interface. +// +// Class RGTListener is not copyable. +class RGTListener : public EmptyTestEventListener { + public: + RGTListener() {} + virtual void OnEnvironmentsSetUpStart(const UnitTest& unit_test); + virtual void OnEnvironmentsTearDownStart(const UnitTest& unit_test); +}; + +void RGTListener::OnEnvironmentsSetUpStart(const UnitTest& unit_test) { +#if GTEST_RGT_RUNTIME_INIT_MANUAL_ + internal::RGT_init_manual(); +#endif +} +void RGTListener::OnEnvironmentsTearDownStart(const UnitTest& unit_test) { + // Possibly report all assertion locations. + internal::RGT_report_all_assertions_to(GTEST_FLAG(report_all_assertions_to)); + + // Report any un-executed test assertions. + int rotten_count = 0; + // FIXME: Is there a way to do this with a range? + internal::RGT_item *item = nullptr; + while ((item = internal::RGT_get_next_rotten_item(item))) { + // Exclude gtest.cc and gtest-port.cc, which contain assertions in + // methods that are rarely used and so show up as rotten. + if (String::EndsWithCaseInsensitive(item->file, "gtest.cc") || + String::EndsWithCaseInsensitive(item->file, "gtest-port.cc")) + continue; + if (GTEST_FLAG(report_rotten)) { + ColoredPrintf(COLOR_RED, "Rotten Test Detected"); + printf(" at %s:%u\n", item->file, item->line); + } + ++rotten_count; + } + + // FIXME: If basically all tests have good reason for un-executed tests, + // this should also be under the flag. + std::string message = "You have " + + FormatCountableNoun(rotten_count, "rotten test assertion", + "rotten test assertions") + " out of " + + FormatCountableNoun(internal::RGT_get_item_count(), + "total assertion", "total assertions") + "\n"; + // Optionally force the test to fail for rotten assertions. + if (rotten_count) { + if (GTEST_FLAG(report_rotten)) + internal::ReportFailureInUnknownLocation(TestPartResult::kNonFatalFailure, + message); + else + ColoredPrintf(COLOR_YELLOW, message.c_str()); + } else { + ColoredPrintf(COLOR_GREEN, message.c_str()); + } +} + +// End RGTListener + // class TestEventRepeater // // This class forwards events to other event listeners. @@ -4443,6 +4512,9 @@ listeners()->Append(new GTEST_CUSTOM_TEST_EVENT_LISTENER_()); #endif // defined(GTEST_CUSTOM_TEST_EVENT_LISTENER_) + // Register the RGT listener. + listeners()->Append(new RGTListener); + #if GTEST_HAS_DEATH_TEST InitDeathTestSubprocessControlInfo(); SuppressTestEventsIfInSubprocess(); @@ -5194,6 +5266,8 @@ " @G--" GTEST_FLAG_PREFIX_ "stream_result_to=@YHOST@G:@YPORT@D\n" " Stream test results to the given server.\n" #endif // GTEST_CAN_STREAM_RESULTS_ +" @G--" GTEST_FLAG_PREFIX_ "report_rotten@D\n" +" Report source location of un-executed test assertions.\n" "\n" "Assertion Behavior:\n" #if GTEST_HAS_DEATH_TEST && !GTEST_OS_WINDOWS @@ -5240,6 +5314,10 @@ ParseBoolFlag(arg, kPrintTimeFlag, >EST_FLAG(print_time)) || ParseInt32Flag(arg, kRandomSeedFlag, >EST_FLAG(random_seed)) || ParseInt32Flag(arg, kRepeatFlag, >EST_FLAG(repeat)) || + ParseBoolFlag(arg, kRGTReportLocation, + >EST_FLAG(report_rotten)) || + ParseStringFlag(arg, kRGTReportAllTo, + >EST_FLAG(report_all_assertions_to)) || ParseBoolFlag(arg, kShuffleFlag, >EST_FLAG(shuffle)) || ParseInt32Flag(arg, kStackTraceDepthFlag, >EST_FLAG(stack_trace_depth)) || Index: llvm/utils/unittest/googletest/src/gtest-internal-inl.h =================================================================== --- llvm/utils/unittest/googletest/src/gtest-internal-inl.h +++ llvm/utils/unittest/googletest/src/gtest-internal-inl.h @@ -96,6 +96,8 @@ const char kPrintTimeFlag[] = "print_time"; const char kRandomSeedFlag[] = "random_seed"; const char kRepeatFlag[] = "repeat"; +const char kRGTReportLocation[] = "report_rotten"; +const char kRGTReportAllTo[] = "report_all_assertions_to"; const char kShuffleFlag[] = "shuffle"; const char kStackTraceDepthFlag[] = "stack_trace_depth"; const char kStreamResultToFlag[] = "stream_result_to"; Index: llvm/utils/unittest/googletest/src/gtest-all.cc =================================================================== --- llvm/utils/unittest/googletest/src/gtest-all.cc +++ llvm/utils/unittest/googletest/src/gtest-all.cc @@ -46,3 +46,4 @@ #include "src/gtest-printers.cc" #include "src/gtest-test-part.cc" #include "src/gtest-typed-test.cc" +#include "src/rgt.cc" Index: llvm/utils/unittest/googletest/include/gtest/internal/rgt.h =================================================================== --- /dev/null +++ llvm/utils/unittest/googletest/include/gtest/internal/rgt.h @@ -0,0 +1,186 @@ +//===- gtest/include/internal/rgt.h - Rotten Green Test helper --*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file defines macros and classes for detecting Rotten Green Tests +// in the LLVM unittests. +// +//===----------------------------------------------------------------------===// +#ifndef GTEST_INCLUDE_GTEST_INTERNAL_RGT_H_ +#define GTEST_INCLUDE_GTEST_INTERNAL_RGT_H_ + +// A Rotten Green Test is one that looks like it is testing some useful +// assertion about code behavior, but in fact the assertion is never +// executed. This file supports instrumenting LLVM unittests to detect +// EXPECT_* and ASSERT_* calls that are not executed, indicating that +// they are Rotten Green Tests. +// +// Inspired by "Rotten Green Tests", Delplanque et al., ICSE 2019 +// DOI 10.1109/ICSE.2019.00062 +// +// The implementation depends on having static data allocated and initialized +// at compile time, in order to identify each test assertion. Execution of +// the assertion will record that test as having been executed. On program +// exit, we scan the array of static data and report those that have not +// been executed. +// +// We allocate the static data into a custom section that has a name that is +// a legal C identifier. This will cause the linker to define symbols for +// the start and end of the section, if those symbols are referenced. This +// allows us to allocate static data piecemeal in the source, and still have +// effectively a single array of all such data at runtime. +// +// This tactic is known to work in GNU linkers and LLD. + +// Due to a quirk of data allocation for items in inline functions, gcc +// won't let us put local static data in a custom section. We use a hack +// to imitate runtime initialization of globals; we can't rely on normal +// initialization of static locals because that requires control to pass +// over the definition of the static local, which defeats the purpose. + +// For MSVC, allocatic static data to a custom section apparently doesn't +// keep it from being optimized away, and there is no equivalent of the +// "used" attribute. So we use a gcc-like hack, but instead of .init_array +// being run automatically on startup, we have to run it manually. + +#if defined(__GNUC__) && !defined(__clang__) +#define GTEST_RGT_RUNTIME_INIT_ARRAY_ 1 +#elif defined(_WIN32) +#define GTEST_RGT_RUNTIME_INIT_MANUAL_ 1 +#endif +#define GTEST_RGT_RUNTIME_INIT_ \ + (GTEST_RGT_RUNTIME_INIT_ARRAY_ || GTEST_RGT_RUNTIME_INIT_MANUAL_) + + +// In cases where a test is known and expected to fail, the client may +// disable checking for the failing test by doing +// #undef GTEST_RGT_DECLARE +// #define GTEST_RGT_DECLARE +// before the test, and re-enabling it afterward with +// #undef GTEST_RGT_DECLARE +// #define GTEST_RGT_DECLARE GTEST_RGT_DECLARE_ +// afterward. +#define GTEST_RGT_DECLARE GTEST_RGT_DECLARE_ + +namespace testing { +namespace internal { + +// The data to record per test-assertion site. +struct RGT_item { + const char *file; + unsigned line; + bool executed; +}; + +#if GTEST_RGT_RUNTIME_INIT_ +// Record the existence of a test point, when we can't arrange for that +// purely with static initialization. +void RGT_record(RGT_item *item); + +#if GTEST_RGT_RUNTIME_INIT_MANUAL_ +// Run all the initializations. +void RGT_init_manual(); + +// Provide a global pointer so we can fake-use all the items. +void *RGT_fake_use = nullptr; +#endif +#endif // GTEST_RGT_RUNTIME_INIT_ + +// Pass nullptr to get the first rotten item; pass the previous return value +// to get the next item. Returns nullptr when there are no more. +// TODO: Is there a reasonable way to achieve this with a range while still +// hiding the inconvenient platform differences? +RGT_item *RGT_get_next_rotten_item(RGT_item *item); + +// Get the total number of test assertions. +int RGT_get_item_count(); + +// For debugging: Report the locations of all test assertions to the +// specified file. +void RGT_report_all_assertions_to(std::string &filename); +} // end namespace internal +} // end namespace testing + +#if GTEST_RGT_RUNTIME_INIT_ + +// Conjure up a function to do startup-time initialization, given that +// we can't arrange for static initialization. + +#if GTEST_RGT_RUNTIME_INIT_ARRAY_ + +#define GTEST_RGT_RECORD_ITEM_(ITEM) \ + struct __rgt_recorder { \ + static void record() { testing::internal::RGT_record(&ITEM); } \ + }; \ + __asm__( \ + ".pushsection .init_array" "\n" \ + ".quad %c0" "\n" \ + ".popsection" "\n" \ + : : "i"(__rgt_recorder::record)); + +#else + +// Windows doesn't automatically provide start/end symbol names, so we roll +// our own with start/end entries. The sorting is determined by suffixes on +// the section name, so we need to paste the base name with another string to +// get correct sorting. Extra fun macro indirection required. + +#define GTEST_RGT_SECTION_NAME_WITH_SUFFIX_(SUFFIX) GTEST_RGT_SECTION_NAME_2(RGT, SUFFIX) +#define GTEST_RGT_SECTION_NAME_2(NAME, SUFFIX) GTEST_RGT_SECTION_NAME_3(NAME, SUFFIX) +#define GTEST_RGT_SECTION_NAME_3(NAME, SUFFIX) GTEST_RGT_SECTION_NAME_4(NAME ## SUFFIX) +#define GTEST_RGT_SECTION_NAME_4(NAME) #NAME + +#define GTEST_RGT_SECTION_NAME_ GTEST_RGT_SECTION_NAME_WITH_SUFFIX_($d) + +// MSVC requires the section to be declared before being used, but simply +// using a global #pragma seems not to work (despite the documentation). +// And because MSVC has no equivalent of attribute(used) we need to fake +// up a pointer escaping so it will look used. + +typedef void(*RGT_recorder)(void); + +#define GTEST_RGT_RECORD_ITEM_(ITEM) \ + struct __rgt_recorder { \ + static void record() { testing::internal::RGT_record(&ITEM); } \ + }; \ + __pragma(section(GTEST_RGT_SECTION_NAME_,read,write)) \ + __declspec(allocate(GTEST_RGT_SECTION_NAME_)) \ + static RGT_recorder __rgt_record_item = __rgt_recorder::record; \ + testing::internal::RGT_fake_use = (void*)&__rgt_record_item; + +#endif // GTEST_RGT_RUNTIME_INIT_ARRAY_ + +#define GTEST_RGT_DECLARE_ \ + static testing::internal::RGT_item \ + GTEST_RGT_item_ { __FILE__, __LINE__, false }; \ + GTEST_RGT_item_.executed = true; \ + GTEST_RGT_RECORD_ITEM_(GTEST_RGT_item_) + +#else + +// The "normal" case, allocate local static data to a custom section +// which we can then iterate over at program end. +// Note, the non-Windows case requires this be a legal C identifier. + +#define GTEST_RGT_SECTION_NAME_ RGT + +// Now define how to decorate the data declarations. +#define GTEST_RGT_SECTION_ATTR_3(NAME) __attribute__((section(#NAME),used)) +#define GTEST_RGT_SECTION_ATTR_2(NAME) GTEST_RGT_SECTION_ATTR_3(NAME) +#define GTEST_RGT_SECTION_ATTR GTEST_RGT_SECTION_ATTR_2(GTEST_RGT_SECTION_NAME_) + + +// Macro to declare one test assertion's data item and initialize it. + +#define GTEST_RGT_DECLARE_ \ + GTEST_RGT_SECTION_ATTR static testing::internal::RGT_item \ + GTEST_RGT_item_ { __FILE__, __LINE__, false }; \ + GTEST_RGT_item_.executed = true; + +#endif // GTEST_RGT_RUNTIME_INIT_ + +#endif // GTEST_INCLUDE_GTEST_INTERNAL_RGT_H_ Index: llvm/utils/unittest/googletest/include/gtest/internal/gtest-internal.h =================================================================== --- llvm/utils/unittest/googletest/include/gtest/internal/gtest-internal.h +++ llvm/utils/unittest/googletest/include/gtest/internal/gtest-internal.h @@ -38,6 +38,7 @@ #define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_INTERNAL_H_ #include "gtest/internal/gtest-port.h" +#include "gtest/internal/rgt.h" #if GTEST_OS_LINUX # include <stdlib.h> @@ -1183,6 +1184,7 @@ // Implements Boolean test assertions such as EXPECT_TRUE. expression can be // either a boolean expression or an AssertionResult. text is a textual // represenation of expression as it was passed into the EXPECT_TRUE. +#if 0 #define GTEST_TEST_BOOLEAN_(expression, text, actual, expected, fail) \ GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ if (const ::testing::AssertionResult gtest_ar_ = \ @@ -1190,7 +1192,17 @@ ; \ else \ fail(::testing::internal::GetBoolAssertionFailureMessage(\ - gtest_ar_, text, #actual, #expected).c_str()) + gtest_ar_, text, #actual, #expected).c_str()) +#else +#define GTEST_TEST_BOOLEAN_(expression, text, actual, expected, fail) \ + GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ + if (const ::testing::AssertionResult gtest_ar_ = \ + ::testing::AssertionResult(expression)) \ + { GTEST_RGT_DECLARE } \ + else \ + fail(::testing::internal::GetBoolAssertionFailureMessage(\ + gtest_ar_, text, #actual, #expected).c_str()) +#endif #define GTEST_TEST_NO_FATAL_FAILURE_(statement, fail) \ GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ Index: llvm/utils/unittest/googletest/include/gtest/gtest_pred_impl.h =================================================================== --- llvm/utils/unittest/googletest/include/gtest/gtest_pred_impl.h +++ llvm/utils/unittest/googletest/include/gtest/gtest_pred_impl.h @@ -75,7 +75,7 @@ #define GTEST_ASSERT_(expression, on_failure) \ GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ if (const ::testing::AssertionResult gtest_ar = (expression)) \ - ; \ + { GTEST_RGT_DECLARE } \ else \ on_failure(gtest_ar.failure_message()) Index: llvm/utils/unittest/googletest/include/gtest/gtest.h =================================================================== --- llvm/utils/unittest/googletest/include/gtest/gtest.h +++ llvm/utils/unittest/googletest/include/gtest/gtest.h @@ -143,6 +143,14 @@ // the specified host machine. GTEST_DECLARE_string_(stream_result_to); +// This flag controls whether we print location info for each +// un-executed test. +GTEST_DECLARE_bool_(report_rotten); + +// When this flag is set with a filename, report all test assertion +// locations to that file. For debugging RGT. +GTEST_DECLARE_string_(report_all_assertions_to); + // The upper limit for valid stack trace depths. const int kMaxStackTraceDepth = 100; Index: llvm/unittests/Support/ProgramTest.cpp =================================================================== --- llvm/unittests/Support/ProgramTest.cpp +++ llvm/unittests/Support/ProgramTest.cpp @@ -123,7 +123,8 @@ MyExe.append(MyAbsExe); StringRef ArgV[] = {MyExe, - "--gtest_filter=ProgramEnvTest.CreateProcessLongPath"}; + "--gtest_filter=ProgramEnvTest.CreateProcessLongPath", + "--gtest_report_rotten=0"}; // Add LLVM_PROGRAM_TEST_LONG_PATH to the environment of the child. addEnvVar("LLVM_PROGRAM_TEST_LONG_PATH=1"); @@ -166,6 +167,7 @@ StringRef argv[] = { my_exe, "--gtest_filter=ProgramEnvTest.CreateProcessTrailingSlash", + "--gtest_report_rotten=0", "-program-test-string-arg1", "has\\\\ trailing\\", "-program-test-string-arg2", @@ -201,7 +203,8 @@ std::string Executable = sys::fs::getMainExecutable(TestMainArgv0, &ProgramTestStringArg1); StringRef argv[] = {Executable, - "--gtest_filter=ProgramEnvTest.TestExecuteNoWait"}; + "--gtest_filter=ProgramEnvTest.TestExecuteNoWait", + "--gtest_report_rotten=0"}; // Add LLVM_PROGRAM_TEST_EXECUTE_NO_WAIT to the environment of the child. addEnvVar("LLVM_PROGRAM_TEST_EXECUTE_NO_WAIT=1"); @@ -255,8 +258,9 @@ std::string Executable = sys::fs::getMainExecutable(TestMainArgv0, &ProgramTestStringArg1); - StringRef argv[] = { - Executable, "--gtest_filter=ProgramEnvTest.TestExecuteAndWaitTimeout"}; + StringRef argv[] = {Executable, + "--gtest_filter=ProgramEnvTest.TestExecuteAndWaitTimeout", + "--gtest_report_rotten=0"}; // Add LLVM_PROGRAM_TEST_TIMEOUT to the environment of the child. addEnvVar("LLVM_PROGRAM_TEST_TIMEOUT=1"); @@ -346,7 +350,9 @@ std::string Executable = sys::fs::getMainExecutable(TestMainArgv0, &ProgramTestStringArg1); StringRef argv[] = { - Executable, "--gtest_filter=ProgramEnvTest.TestExecuteAndWaitStatistics"}; + Executable, + "--gtest_filter=ProgramEnvTest.TestExecuteAndWaitStatistics", + "--gtest_report_rotten=0"}; // Add LLVM_PROGRAM_TEST_STATISTICS to the environment of the child. addEnvVar("LLVM_PROGRAM_TEST_STATISTICS=1"); Index: llvm/test/Unit/lit.cfg.py =================================================================== --- llvm/test/Unit/lit.cfg.py +++ llvm/test/Unit/lit.cfg.py @@ -24,6 +24,9 @@ # testFormat: The test format to use to interpret tests. config.test_format = lit.formats.GoogleTest(config.llvm_build_mode, 'Tests') +# Tell gtest not to report un-executed tests. +config.environment['GTEST_REPORT_ROTTEN'] = '0' + # Propagate the temp directory. Windows requires this because it uses \Windows\ # if none of these are present. if 'TMP' in os.environ: Index: clang/unittests/Tooling/Syntax/MutationsTest.cpp =================================================================== --- clang/unittests/Tooling/Syntax/MutationsTest.cpp +++ clang/unittests/Tooling/Syntax/MutationsTest.cpp @@ -41,6 +41,9 @@ EXPECT_EQ(Expected, *Output) << "input is:\n" << Input; }; +// GCC seems to have issues here. +#undef GTEST_RGT_DECLARE +#define GTEST_RGT_DECLARE // Removes the selected statement. Input should have exactly one selected // range and it should correspond to a single statement. Transformation RemoveStatement = [this](const llvm::Annotations &Input, @@ -52,6 +55,8 @@ EXPECT_FALSE(S->isOriginal()) << "node removed from tree cannot be marked as original"; }; +#undef GTEST_RGT_DECLARE +#define GTEST_RGT_DECLARE GTEST_RGT_DECLARE_ }; INSTANTIATE_TEST_CASE_P(SyntaxTreeTests, MutationTest, Index: clang/unittests/ASTMatchers/ASTMatchersInternalTest.cpp =================================================================== --- clang/unittests/ASTMatchers/ASTMatchersInternalTest.cpp +++ clang/unittests/ASTMatchers/ASTMatchersInternalTest.cpp @@ -21,6 +21,10 @@ using internal::DynTypedMatcher; #if GTEST_HAS_DEATH_TEST +// RGT-FIXME: The RGT infraststructure does not play nicely with ASSERT_DEATH +// when the statement lexically contains an EXPECT. +#undef GTEST_RGT_DECLARE +#define GTEST_RGT_DECLARE TEST(HasNameDeathTest, DiesOnEmptyName) { ASSERT_DEBUG_DEATH({ DeclarationMatcher HasEmptyName = recordDecl(hasName("")); @@ -34,6 +38,8 @@ EXPECT_TRUE(notMatches("class X {};", HasEmptyName)); }, ""); } +#undef GTEST_RGT_DECLARE +#define GTEST_RGT_DECLARE GTEST_RGT_DECLARE_ #endif TEST(ConstructVariadic, MismatchedTypes_Regression) { Index: clang/test/Unit/lit.cfg.py =================================================================== --- clang/test/Unit/lit.cfg.py +++ clang/test/Unit/lit.cfg.py @@ -23,6 +23,9 @@ # testFormat: The test format to use to interpret tests. config.test_format = lit.formats.GoogleTest(config.llvm_build_mode, 'Tests') +# Tell gtest not to report un-executed tests. +config.environment['GTEST_REPORT_ROTTEN'] = '0' + # Propagate the temp directory. Windows requires this because it uses \Windows\ # if none of these are present. if 'TMP' in os.environ:
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits