Skip to content

Commit 5450174

Browse files
zhanyongwancopybara-github
authored andcommitted
Adds support for a --gtest_fail_if_no_test_linked flag
to fail the test program if no test case is linked in (a common programmer mistake). PiperOrigin-RevId: 730571311 Change-Id: I1dab04adfe35581274d0b4ec79a017014d50e1ea
1 parent 3fbe4db commit 5450174

6 files changed

+270
-16
lines changed

docs/advanced.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1929,6 +1929,20 @@ the `--gtest_also_run_disabled_tests` flag or set the
19291929
You can combine this with the `--gtest_filter` flag to further select which
19301930
disabled tests to run.
19311931
1932+
### Enforcing Having At Least One Test Case
1933+
1934+
A not uncommon programmer mistake is to write a test program that has no test
1935+
case linked in. This can happen, for example, when you put test case definitions
1936+
in a library and the library is not marked as "always link".
1937+
1938+
To catch such mistakes, run the test program with the
1939+
`--gtest_fail_if_no_test_linked` flag or set the `GTEST_FAIL_IF_NO_TEST_LINKED`
1940+
environment variable to a value other than `0`. Now the program will fail if no
1941+
test case is linked in.
1942+
1943+
Note that *any* test case linked in makes the program valid for the purpose of
1944+
this check. In particular, even a disabled test case suffices.
1945+
19321946
### Repeating the Tests
19331947
19341948
Once in a while you'll run into a test whose result is hit-or-miss. Perhaps it

googletest/src/gtest.cc

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,12 @@ GTEST_DEFINE_bool_(
258258
testing::GetDefaultFailFast()),
259259
"True if and only if a test failure should stop further test execution.");
260260

261+
GTEST_DEFINE_bool_(
262+
fail_if_no_test_linked,
263+
testing::internal::BoolFromGTestEnv("fail_if_no_test_linked", false),
264+
"True if and only if the test should fail if no test case (including "
265+
"disabled test cases) is linked.");
266+
261267
GTEST_DEFINE_bool_(
262268
also_run_disabled_tests,
263269
testing::internal::BoolFromGTestEnv("also_run_disabled_tests", false),
@@ -5890,6 +5896,14 @@ bool UnitTestImpl::RunAllTests() {
58905896
// user didn't call InitGoogleTest.
58915897
PostFlagParsingInit();
58925898

5899+
if (GTEST_FLAG_GET(fail_if_no_test_linked) && total_test_count() == 0) {
5900+
ColoredPrintf(
5901+
GTestColor::kRed,
5902+
"This test program does NOT link in any test case. This is INVALID. "
5903+
"Please make sure to link in at least one test case.\n");
5904+
return false;
5905+
}
5906+
58935907
#if GTEST_HAS_FILE_SYSTEM
58945908
// Even if sharding is not on, test runners may want to use the
58955909
// GTEST_SHARD_STATUS_FILE to query whether the test supports the sharding
@@ -6677,6 +6691,7 @@ static bool ParseGoogleTestFlag(const char* const arg) {
66776691
GTEST_INTERNAL_PARSE_FLAG(death_test_style);
66786692
GTEST_INTERNAL_PARSE_FLAG(death_test_use_fork);
66796693
GTEST_INTERNAL_PARSE_FLAG(fail_fast);
6694+
GTEST_INTERNAL_PARSE_FLAG(fail_if_no_test_linked);
66806695
GTEST_INTERNAL_PARSE_FLAG(filter);
66816696
GTEST_INTERNAL_PARSE_FLAG(internal_run_death_test);
66826697
GTEST_INTERNAL_PARSE_FLAG(list_tests);

googletest/test/BUILD.bazel

Lines changed: 51 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -47,33 +47,36 @@ cc_test(
4747
"*.h",
4848
],
4949
exclude = [
50-
"gtest-unittest-api_test.cc",
51-
"googletest/src/gtest-all.cc",
52-
"gtest_all_test.cc",
53-
"gtest-death-test_ex_test.cc",
54-
"gtest-listener_test.cc",
55-
"gtest-unittest-api_test.cc",
56-
"googletest-param-test-test.cc",
57-
"googletest-param-test2-test.cc",
50+
# go/keep-sorted start
51+
"googletest-break-on-failure-unittest_.cc",
5852
"googletest-catch-exceptions-test_.cc",
5953
"googletest-color-test_.cc",
54+
"googletest-death-test_ex_test.cc",
6055
"googletest-env-var-test_.cc",
56+
"googletest-fail-if-no-test-linked-test-with-disabled-test_.cc",
57+
"googletest-fail-if-no-test-linked-test-with-enabled-test_.cc",
6158
"googletest-failfast-unittest_.cc",
6259
"googletest-filter-unittest_.cc",
6360
"googletest-global-environment-unittest_.cc",
64-
"googletest-break-on-failure-unittest_.cc",
61+
"googletest-list-tests-unittest_.cc",
6562
"googletest-listener-test.cc",
6663
"googletest-message-test.cc",
6764
"googletest-output-test_.cc",
68-
"googletest-list-tests-unittest_.cc",
69-
"googletest-shuffle-test_.cc",
70-
"googletest-setuptestsuite-test_.cc",
71-
"googletest-uninitialized-test_.cc",
72-
"googletest-death-test_ex_test.cc",
73-
"googletest-param-test-test",
74-
"googletest-throw-on-failure-test_.cc",
7565
"googletest-param-test-invalid-name1-test_.cc",
7666
"googletest-param-test-invalid-name2-test_.cc",
67+
"googletest-param-test-test",
68+
"googletest-param-test-test.cc",
69+
"googletest-param-test2-test.cc",
70+
"googletest-setuptestsuite-test_.cc",
71+
"googletest-shuffle-test_.cc",
72+
"googletest-throw-on-failure-test_.cc",
73+
"googletest-uninitialized-test_.cc",
74+
"googletest/src/gtest-all.cc",
75+
"gtest-death-test_ex_test.cc",
76+
"gtest-listener_test.cc",
77+
"gtest-unittest-api_test.cc",
78+
"gtest_all_test.cc",
79+
# go/keep-sorted end
7780
],
7881
) + select({
7982
"//:windows": [],
@@ -323,6 +326,26 @@ cc_binary(
323326
deps = ["//:gtest"],
324327
)
325328

329+
cc_binary(
330+
name = "googletest-fail-if-no-test-linked-test-without-test_",
331+
testonly = 1,
332+
deps = ["//:gtest_main"],
333+
)
334+
335+
cc_binary(
336+
name = "googletest-fail-if-no-test-linked-test-with-disabled-test_",
337+
testonly = 1,
338+
srcs = ["googletest-fail-if-no-test-linked-test-with-disabled-test_.cc"],
339+
deps = ["//:gtest_main"],
340+
)
341+
342+
cc_binary(
343+
name = "googletest-fail-if-no-test-linked-test-with-enabled-test_",
344+
testonly = 1,
345+
srcs = ["googletest-fail-if-no-test-linked-test-with-enabled-test_.cc"],
346+
deps = ["//:gtest_main"],
347+
)
348+
326349
cc_test(
327350
name = "gtest_skip_test",
328351
size = "small",
@@ -363,6 +386,18 @@ py_test(
363386
deps = [":gtest_test_utils"],
364387
)
365388

389+
py_test(
390+
name = "googletest-fail-if-no-test-linked-test",
391+
size = "small",
392+
srcs = ["googletest-fail-if-no-test-linked-test.py"],
393+
data = [
394+
":googletest-fail-if-no-test-linked-test-with-disabled-test_",
395+
":googletest-fail-if-no-test-linked-test-with-enabled-test_",
396+
":googletest-fail-if-no-test-linked-test-without-test_",
397+
],
398+
deps = [":gtest_test_utils"],
399+
)
400+
366401
cc_binary(
367402
name = "googletest-shuffle-test_",
368403
srcs = ["googletest-shuffle-test_.cc"],
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// Copyright 2025, Google Inc.
2+
// All rights reserved.
3+
//
4+
// Redistribution and use in source and binary forms, with or without
5+
// modification, are permitted provided that the following conditions are
6+
// met:
7+
//
8+
// * Redistributions of source code must retain the above copyright
9+
// notice, this list of conditions and the following disclaimer.
10+
// * Redistributions in binary form must reproduce the above
11+
// copyright notice, this list of conditions and the following disclaimer
12+
// in the documentation and/or other materials provided with the
13+
// distribution.
14+
// * Neither the name of Google Inc. nor the names of its
15+
// contributors may be used to endorse or promote products derived from
16+
// this software without specific prior written permission.
17+
//
18+
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19+
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20+
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21+
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22+
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23+
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24+
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25+
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26+
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27+
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28+
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29+
30+
// Unit test for Google Test's --gtest_fail_if_no_test_linked flag.
31+
//
32+
// This program will be invoked from a Python test.
33+
// Don't run it directly.
34+
35+
#include "gtest/gtest.h"
36+
37+
// A dummy test that is disabled.
38+
TEST(SomeTest, DISABLED_Test1) {}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// Copyright 2025, Google Inc.
2+
// All rights reserved.
3+
//
4+
// Redistribution and use in source and binary forms, with or without
5+
// modification, are permitted provided that the following conditions are
6+
// met:
7+
//
8+
// * Redistributions of source code must retain the above copyright
9+
// notice, this list of conditions and the following disclaimer.
10+
// * Redistributions in binary form must reproduce the above
11+
// copyright notice, this list of conditions and the following disclaimer
12+
// in the documentation and/or other materials provided with the
13+
// distribution.
14+
// * Neither the name of Google Inc. nor the names of its
15+
// contributors may be used to endorse or promote products derived from
16+
// this software without specific prior written permission.
17+
//
18+
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19+
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20+
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21+
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22+
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23+
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24+
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25+
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26+
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27+
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28+
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29+
30+
// Unit test for Google Test's --gtest_fail_if_no_test_linked flag.
31+
//
32+
// This program will be invoked from a Python test.
33+
// Don't run it directly.
34+
35+
#include "gtest/gtest.h"
36+
37+
// A dummy test that is enabled.
38+
TEST(SomeTest, Test1) {}
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
#!/usr/bin/env python3 # pylint: disable=g-interpreter-mismatch
2+
#
3+
# Copyright 2025, Google Inc.
4+
# All rights reserved.
5+
#
6+
# Redistribution and use in source and binary forms, with or without
7+
# modification, are permitted provided that the following conditions are
8+
# met:
9+
#
10+
# * Redistributions of source code must retain the above copyright
11+
# notice, this list of conditions and the following disclaimer.
12+
# * Redistributions in binary form must reproduce the above
13+
# copyright notice, this list of conditions and the following disclaimer
14+
# in the documentation and/or other materials provided with the
15+
# distribution.
16+
# * Neither the name of Google Inc. nor the names of its
17+
# contributors may be used to endorse or promote products derived from
18+
# this software without specific prior written permission.
19+
#
20+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21+
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22+
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23+
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24+
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25+
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26+
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27+
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28+
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29+
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30+
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31+
32+
"""Tests for Google Test's --gtest_fail_if_no_test_linked flag."""
33+
34+
from googletest.test import gtest_test_utils
35+
36+
# The command line flag for enabling the fail-if-no-test-linked behavior.
37+
FAIL_IF_NO_TEST_LINKED_FLAG = "gtest_fail_if_no_test_linked"
38+
39+
40+
class GTestFailIfNoTestLinkedTest(gtest_test_utils.TestCase):
41+
"""Tests the --gtest_fail_if_no_test_linked flag."""
42+
43+
def Run(self, program_name, flag=None):
44+
"""Run the given program with the given flag.
45+
46+
Args:
47+
program_name: Name of the program to run.
48+
flag: The command line flag to pass to the program, or None.
49+
50+
Returns:
51+
True if the program exits with code 0, false otherwise.
52+
"""
53+
54+
exe_path = gtest_test_utils.GetTestExecutablePath(program_name)
55+
args = [exe_path]
56+
if flag is not None:
57+
args += [flag]
58+
process = gtest_test_utils.Subprocess(args, capture_stderr=False)
59+
return process.exited and process.exit_code == 0
60+
61+
def testSucceedsIfNoTestLinkedAndFlagNotSpecified(self):
62+
"""Tests the behavior of no test linked and flag not specified."""
63+
64+
self.assertTrue(
65+
self.Run("googletest-fail-if-no-test-linked-test-without-test_")
66+
)
67+
68+
def testFailsIfNoTestLinkedAndFlagSpecified(self):
69+
"""Tests the behavior of no test linked and flag specified."""
70+
71+
self.assertFalse(
72+
self.Run(
73+
"googletest-fail-if-no-test-linked-test-without-test_",
74+
f"--{FAIL_IF_NO_TEST_LINKED_FLAG}",
75+
)
76+
)
77+
78+
def testSucceedsIfEnabledTestLinkedAndFlagNotSpecified(self):
79+
"""Tests the behavior of enabled test linked and flag not specified."""
80+
81+
self.assertTrue(
82+
self.Run("googletest-fail-if-no-test-linked-test-with-enabled-test_")
83+
)
84+
85+
def testSucceedsIfEnabledTestLinkedAndFlagSpecified(self):
86+
"""Tests the behavior of enabled test linked and flag specified."""
87+
88+
self.assertTrue(
89+
self.Run(
90+
"googletest-fail-if-no-test-linked-test-with-enabled-test_",
91+
f"--{FAIL_IF_NO_TEST_LINKED_FLAG}",
92+
)
93+
)
94+
95+
def testSucceedsIfDisabledTestLinkedAndFlagNotSpecified(self):
96+
"""Tests the behavior of disabled test linked and flag not specified."""
97+
98+
self.assertTrue(
99+
self.Run("googletest-fail-if-no-test-linked-test-with-disabled-test_")
100+
)
101+
102+
def testSucceedsIfDisabledTestLinkedAndFlagSpecified(self):
103+
"""Tests the behavior of disabled test linked and flag specified."""
104+
105+
self.assertTrue(
106+
self.Run(
107+
"googletest-fail-if-no-test-linked-test-with-disabled-test_",
108+
f"--{FAIL_IF_NO_TEST_LINKED_FLAG}",
109+
)
110+
)
111+
112+
113+
if __name__ == "__main__":
114+
gtest_test_utils.Main()

0 commit comments

Comments
 (0)