From 24a9e940d481f992ba852599c78bb2217362847b Mon Sep 17 00:00:00 2001
From: Derek Mauro <dmauro@google.com>
Date: Fri, 28 Feb 2025 11:35:40 -0800
Subject: [PATCH] Try to warn the user when test filters do not match any tests

PiperOrigin-RevId: 732204780
Change-Id: I2c4ccabd123e3b79c3dd8bc768a4cd9a576d282c
---
 googletest/src/gtest-internal-inl.h           |  4 +++
 googletest/src/gtest.cc                       | 35 +++++++++++++++++++
 googletest/test/googletest-filter-unittest.py | 19 ++++++++++
 3 files changed, 58 insertions(+)

diff --git a/googletest/src/gtest-internal-inl.h b/googletest/src/gtest-internal-inl.h
index cc6f0048..6a39b93b 100644
--- a/googletest/src/gtest-internal-inl.h
+++ b/googletest/src/gtest-internal-inl.h
@@ -826,6 +826,10 @@ class GTEST_API_ UnitTestImpl {
   bool catch_exceptions() const { return catch_exceptions_; }
 
  private:
+  // Returns true if a warning should be issued if no tests match the test
+  // filter flag.
+  bool ShouldWarnIfNoTestsMatchFilter() const;
+
   struct CompareTestSuitesByPointer {
     bool operator()(const TestSuite* lhs, const TestSuite* rhs) const {
       return lhs->name_ < rhs->name_;
diff --git a/googletest/src/gtest.cc b/googletest/src/gtest.cc
index 26959dfc..09af1517 100644
--- a/googletest/src/gtest.cc
+++ b/googletest/src/gtest.cc
@@ -6113,6 +6113,17 @@ bool UnitTestImpl::RunAllTests() {
     environments_.clear();
   }
 
+  // Try to warn the user if no tests matched the test filter.
+  if (ShouldWarnIfNoTestsMatchFilter()) {
+    const std::string filter_warning =
+        std::string("filter \"") + GTEST_FLAG_GET(filter) +
+        "\" did not match any test; no tests were run\n";
+    ColoredPrintf(GTestColor::kRed, "WARNING: %s", filter_warning.c_str());
+#if GTEST_HAS_FILE_SYSTEM
+    AppendToTestWarningsOutputFile(filter_warning);
+#endif  // GTEST_HAS_FILE_SYSTEM
+  }
+
   if (!gtest_is_initialized_before_run_all_tests) {
     ColoredPrintf(
         GTestColor::kRed,
@@ -6281,6 +6292,30 @@ int UnitTestImpl::FilterTests(ReactionToSharding shard_tests) {
   return num_selected_tests;
 }
 
+// Returns true if a warning should be issued if no tests match the test filter
+// flag. We can't simply count the number of tests that ran because, for
+// instance, test sharding and death tests might mean no tests are expected to
+// run in this process, but will run in another process.
+bool UnitTestImpl::ShouldWarnIfNoTestsMatchFilter() const {
+  if (total_test_count() == 0) {
+    // No tests were linked in to program.
+    // This case is handled by a different warning.
+    return false;
+  }
+  const PositiveAndNegativeUnitTestFilter gtest_flag_filter(
+      GTEST_FLAG_GET(filter));
+  for (auto* test_suite : test_suites_) {
+    const std::string& test_suite_name = test_suite->name_;
+    for (TestInfo* test_info : test_suite->test_info_list()) {
+      const std::string& test_name = test_info->name_;
+      if (gtest_flag_filter.MatchesTest(test_suite_name, test_name)) {
+        return false;
+      }
+    }
+  }
+  return true;
+}
+
 // Prints the given C-string on a single line by replacing all '\n'
 // characters with string "\\n".  If the output takes more than
 // max_length characters, only prints the first max_length characters
diff --git a/googletest/test/googletest-filter-unittest.py b/googletest/test/googletest-filter-unittest.py
index f1f3c7a5..a44882a6 100755
--- a/googletest/test/googletest-filter-unittest.py
+++ b/googletest/test/googletest-filter-unittest.py
@@ -97,6 +97,9 @@ TOTAL_SHARDS_ENV_VAR = 'GTEST_TOTAL_SHARDS'
 SHARD_INDEX_ENV_VAR = 'GTEST_SHARD_INDEX'
 SHARD_STATUS_FILE_ENV_VAR = 'GTEST_SHARD_STATUS_FILE'
 
+# The environment variable for the test warnings output file.
+TEST_WARNINGS_OUTPUT_FILE = 'TEST_WARNINGS_OUTPUT_FILE'
+
 # The command line flag for specifying the test filters.
 FILTER_FLAG = 'gtest_filter'
 
@@ -419,6 +422,22 @@ class GTestFilterUnitTest(gtest_test_utils.TestCase):
     self.RunAndVerify('BadFilter', [])
     self.RunAndVerifyAllowingDisabled('BadFilter', [])
 
+  def testBadFilterWithWarningFile(self):
+    """Tests the warning file when a filter that matches nothing."""
+
+    warning_file = os.path.join(
+        gtest_test_utils.GetTempDir(), 'testBadFilterWithWarningFile'
+    )
+    extra_env = {TEST_WARNINGS_OUTPUT_FILE: warning_file}
+    args = ['--%s=%s' % (FILTER_FLAG, 'BadFilter')]
+    InvokeWithModifiedEnv(extra_env, RunAndReturnOutput, args)
+    with open(warning_file, 'r') as f:
+      warning_file_contents = f.read()
+      self.assertEqual(
+          warning_file_contents,
+          'filter "BadFilter" did not match any test; no tests were run\n',
+      )
+
   def testFullName(self):
     """Tests filtering by full name."""