Output to the test warning file if no tests are linked.

Bazel sets the environment variable TEST_WARNINGS_OUTPUT_FILE
https://bazel.build/reference/test-encyclopedia#initial-conditions

If no tests are linked and the new flag --gtest_fail_if_no_test_linked
is not true (which is the current default), we can still warn the user
as this may be a programming error without failing the test (which would
break existing users).

PiperOrigin-RevId: 731402363
Change-Id: Ia481689efd4bd18889feaaa38bc56049a3f651cd
This commit is contained in:
Derek Mauro 2025-02-26 11:56:21 -08:00 committed by Copybara-Service
parent 54501746a6
commit e5669fdffc
2 changed files with 103 additions and 12 deletions

View File

@ -192,12 +192,17 @@ static const char kDefaultOutputFormat[] = "xml";
// The default output file. // The default output file.
static const char kDefaultOutputFile[] = "test_detail"; static const char kDefaultOutputFile[] = "test_detail";
// These environment variables are set by Bazel.
// https://bazel.build/reference/test-encyclopedia#initial-conditions
//
// The environment variable name for the test shard index. // The environment variable name for the test shard index.
static const char kTestShardIndex[] = "GTEST_SHARD_INDEX"; static const char kTestShardIndex[] = "GTEST_SHARD_INDEX";
// The environment variable name for the total number of test shards. // The environment variable name for the total number of test shards.
static const char kTestTotalShards[] = "GTEST_TOTAL_SHARDS"; static const char kTestTotalShards[] = "GTEST_TOTAL_SHARDS";
// The environment variable name for the test shard status file. // The environment variable name for the test shard status file.
static const char kTestShardStatusFile[] = "GTEST_SHARD_STATUS_FILE"; static const char kTestShardStatusFile[] = "GTEST_SHARD_STATUS_FILE";
// The environment variable name for the test output warnings file.
static const char kTestWarningsOutputFile[] = "TEST_WARNINGS_OUTPUT_FILE";
namespace internal { namespace internal {
@ -5875,6 +5880,23 @@ TestSuite* UnitTestImpl::GetTestSuite(
static void SetUpEnvironment(Environment* env) { env->SetUp(); } static void SetUpEnvironment(Environment* env) { env->SetUp(); }
static void TearDownEnvironment(Environment* env) { env->TearDown(); } static void TearDownEnvironment(Environment* env) { env->TearDown(); }
// If the environment variable TEST_WARNINGS_OUTPUT_FILE was provided, appends
// `str` to the file, creating the file if necessary.
#if GTEST_HAS_FILE_SYSTEM
static void AppendToTestWarningsOutputFile(const std::string& str) {
const char* const filename = posix::GetEnv(kTestWarningsOutputFile);
if (filename == nullptr) {
return;
}
auto* const file = posix::FOpen(filename, "a");
if (file == nullptr) {
return;
}
GTEST_CHECK_(fwrite(str.data(), 1, str.size(), file) == str.size());
GTEST_CHECK_(posix::FClose(file) == 0);
}
#endif // GTEST_HAS_FILE_SYSTEM
// Runs all tests in this UnitTest object, prints the result, and // Runs all tests in this UnitTest object, prints the result, and
// returns true if all tests are successful. If any exception is // returns true if all tests are successful. If any exception is
// thrown during a test, the test is considered to be failed, but the // thrown during a test, the test is considered to be failed, but the
@ -5896,12 +5918,26 @@ bool UnitTestImpl::RunAllTests() {
// user didn't call InitGoogleTest. // user didn't call InitGoogleTest.
PostFlagParsingInit(); PostFlagParsingInit();
if (GTEST_FLAG_GET(fail_if_no_test_linked) && total_test_count() == 0) { // Handle the case where the program has no tests linked.
// Sometimes this is a programmer mistake, but sometimes it is intended.
if (total_test_count() == 0) {
constexpr char kNoTestLinkedMessage[] =
"This test program does NOT link in any test case.";
constexpr char kNoTestLinkedFatal[] =
"This is INVALID. Please make sure to link in at least one test case.";
constexpr char kNoTestLinkedWarning[] =
"Please make sure this is intended.";
const bool fail_if_no_test_linked = GTEST_FLAG_GET(fail_if_no_test_linked);
ColoredPrintf( ColoredPrintf(
GTestColor::kRed, GTestColor::kRed, "%s %s\n", kNoTestLinkedMessage,
"This test program does NOT link in any test case. This is INVALID. " fail_if_no_test_linked ? kNoTestLinkedFatal : kNoTestLinkedWarning);
"Please make sure to link in at least one test case.\n"); if (fail_if_no_test_linked) {
return false; return false;
}
#if GTEST_HAS_FILE_SYSTEM
AppendToTestWarningsOutputFile(std::string(kNoTestLinkedMessage) + ' ' +
kNoTestLinkedWarning + '\n');
#endif // GTEST_HAS_FILE_SYSTEM
} }
#if GTEST_HAS_FILE_SYSTEM #if GTEST_HAS_FILE_SYSTEM

View File

@ -31,21 +31,26 @@
"""Tests for Google Test's --gtest_fail_if_no_test_linked flag.""" """Tests for Google Test's --gtest_fail_if_no_test_linked flag."""
import os
from googletest.test import gtest_test_utils from googletest.test import gtest_test_utils
# The command line flag for enabling the fail-if-no-test-linked behavior. # The command line flag for enabling the fail-if-no-test-linked behavior.
FAIL_IF_NO_TEST_LINKED_FLAG = "gtest_fail_if_no_test_linked" FAIL_IF_NO_TEST_LINKED_FLAG = "gtest_fail_if_no_test_linked"
# The environment variable for the test output warnings file.
TEST_WARNINGS_OUTPUT_FILE = "TEST_WARNINGS_OUTPUT_FILE"
class GTestFailIfNoTestLinkedTest(gtest_test_utils.TestCase): class GTestFailIfNoTestLinkedTest(gtest_test_utils.TestCase):
"""Tests the --gtest_fail_if_no_test_linked flag.""" """Tests the --gtest_fail_if_no_test_linked flag."""
def Run(self, program_name, flag=None): def Run(self, program_name, flag=None, env=None):
"""Run the given program with the given flag. """Run the given program with the given flag.
Args: Args:
program_name: Name of the program to run. program_name: Name of the program to run.
flag: The command line flag to pass to the program, or None. flag: The command line flag to pass to the program, or None.
env: Dictionary with environment to pass to the subprocess.
Returns: Returns:
True if the program exits with code 0, false otherwise. True if the program exits with code 0, false otherwise.
@ -55,59 +60,109 @@ class GTestFailIfNoTestLinkedTest(gtest_test_utils.TestCase):
args = [exe_path] args = [exe_path]
if flag is not None: if flag is not None:
args += [flag] args += [flag]
process = gtest_test_utils.Subprocess(args, capture_stderr=False) process = gtest_test_utils.Subprocess(args, capture_stderr=False, env=env)
return process.exited and process.exit_code == 0 return process.exited and process.exit_code == 0
def testSucceedsIfNoTestLinkedAndFlagNotSpecified(self): def testSucceedsIfNoTestLinkedAndFlagNotSpecified(self):
"""Tests the behavior of no test linked and flag not specified.""" """Tests the behavior of no test linked and flag not specified."""
self.assertTrue( self.assertTrue(
self.Run("googletest-fail-if-no-test-linked-test-without-test_") self.Run("googletest-fail-if-no-test-linked-test-without-test_")
) )
def testSucceedsIfNoTestLinkedAndFlagNotSpecifiedWithWarningFile(self):
"""Tests that no test linked results in warning file output."""
warning_file = os.path.join(gtest_test_utils.GetTempDir(), "NO_TEST_LINKED")
self.assertTrue(
self.Run(
"googletest-fail-if-no-test-linked-test-without-test_",
env={TEST_WARNINGS_OUTPUT_FILE: warning_file},
)
)
warning_file_contents = open(warning_file, "r").read()
self.assertEqual(
warning_file_contents,
"This test program does NOT link in any test case. Please make sure"
" this is intended.\n",
)
def testFailsIfNoTestLinkedAndFlagSpecified(self): def testFailsIfNoTestLinkedAndFlagSpecified(self):
"""Tests the behavior of no test linked and flag specified.""" """Tests the behavior of no test linked and flag specified."""
warning_file = os.path.join(
gtest_test_utils.GetTempDir(), "SHOULD_NOT_EXIST"
)
self.assertFalse( self.assertFalse(
self.Run( self.Run(
"googletest-fail-if-no-test-linked-test-without-test_", "googletest-fail-if-no-test-linked-test-without-test_",
f"--{FAIL_IF_NO_TEST_LINKED_FLAG}", f"--{FAIL_IF_NO_TEST_LINKED_FLAG}",
env={TEST_WARNINGS_OUTPUT_FILE: warning_file},
) )
) )
with self.assertRaises(FileNotFoundError):
open(warning_file, "r")
def testSucceedsIfEnabledTestLinkedAndFlagNotSpecified(self): def testSucceedsIfEnabledTestLinkedAndFlagNotSpecified(self):
"""Tests the behavior of enabled test linked and flag not specified.""" """Tests the behavior of enabled test linked and flag not specified."""
self.assertTrue( warning_file = os.path.join(
self.Run("googletest-fail-if-no-test-linked-test-with-enabled-test_") gtest_test_utils.GetTempDir(), "SHOULD_NOT_EXIST"
) )
self.assertTrue(
self.Run(
"googletest-fail-if-no-test-linked-test-with-enabled-test_",
env={TEST_WARNINGS_OUTPUT_FILE: warning_file},
)
)
with self.assertRaises(FileNotFoundError):
open(warning_file, "r")
def testSucceedsIfEnabledTestLinkedAndFlagSpecified(self): def testSucceedsIfEnabledTestLinkedAndFlagSpecified(self):
"""Tests the behavior of enabled test linked and flag specified.""" """Tests the behavior of enabled test linked and flag specified."""
warning_file = os.path.join(
gtest_test_utils.GetTempDir(), "SHOULD_NOT_EXIST"
)
self.assertTrue( self.assertTrue(
self.Run( self.Run(
"googletest-fail-if-no-test-linked-test-with-enabled-test_", "googletest-fail-if-no-test-linked-test-with-enabled-test_",
f"--{FAIL_IF_NO_TEST_LINKED_FLAG}", f"--{FAIL_IF_NO_TEST_LINKED_FLAG}",
env={TEST_WARNINGS_OUTPUT_FILE: warning_file},
) )
) )
with self.assertRaises(FileNotFoundError):
open(warning_file, "r")
def testSucceedsIfDisabledTestLinkedAndFlagNotSpecified(self): def testSucceedsIfDisabledTestLinkedAndFlagNotSpecified(self):
"""Tests the behavior of disabled test linked and flag not specified.""" """Tests the behavior of disabled test linked and flag not specified."""
self.assertTrue( warning_file = os.path.join(
self.Run("googletest-fail-if-no-test-linked-test-with-disabled-test_") gtest_test_utils.GetTempDir(), "SHOULD_NOT_EXIST"
) )
self.assertTrue(
self.Run(
"googletest-fail-if-no-test-linked-test-with-disabled-test_",
env={TEST_WARNINGS_OUTPUT_FILE: warning_file},
)
)
with self.assertRaises(FileNotFoundError):
open(warning_file, "r")
def testSucceedsIfDisabledTestLinkedAndFlagSpecified(self): def testSucceedsIfDisabledTestLinkedAndFlagSpecified(self):
"""Tests the behavior of disabled test linked and flag specified.""" """Tests the behavior of disabled test linked and flag specified."""
warning_file = os.path.join(
gtest_test_utils.GetTempDir(), "SHOULD_NOT_EXIST"
)
self.assertTrue( self.assertTrue(
self.Run( self.Run(
"googletest-fail-if-no-test-linked-test-with-disabled-test_", "googletest-fail-if-no-test-linked-test-with-disabled-test_",
f"--{FAIL_IF_NO_TEST_LINKED_FLAG}", f"--{FAIL_IF_NO_TEST_LINKED_FLAG}",
env={TEST_WARNINGS_OUTPUT_FILE: warning_file},
) )
) )
with self.assertRaises(FileNotFoundError):
open(warning_file, "r")
if __name__ == "__main__": if __name__ == "__main__":