Compare commits

...

6 Commits

Author SHA1 Message Date
Christian Clauss
16926d0c40
Merge ff20316ec972fff47c63c7c1eba18d83a9655e55 into 7140cd416cecd7462a8aae488024abeee55598e4 2026-06-11 19:28:10 +02:00
Mike Kruskal
7140cd416c Flip the recommendation about using EXPECT statements inside custom matchers.
PiperOrigin-RevId: 925686474
Change-Id: I7b5d7e82dc8618d6914844446fa260468b947ece
2026-06-02 18:03:04 -07:00
Abseil Team
a721f1b20c Automated Code Change
PiperOrigin-RevId: 924116072
Change-Id: I3e04ebbff65c897e50c37e1a47a2018f31382104
2026-05-30 20:49:30 -07:00
Aaron Jacobs
8736d2cd5c gmock-spec-builders: support mocking const-qualified function types.
In particular this automatically gives us support for examples like the
following:

    using SomeFn = absl::AnyInvocable<R(Args...) const>;
    MockFunction<SomeFn> some_fn;

PiperOrigin-RevId: 921303527
Change-Id: I19bf59671781e85db65cc20c0d6ea10b056c528a
2026-05-26 01:30:54 -07:00
Abseil Team
09f45f51fb Mark gtest_dt_ptr as const.
PiperOrigin-RevId: 920667027
Change-Id: I3dd71c96e206eb19bf3e62bb08ce7043d83fb526
2026-05-24 16:31:25 -07:00
Christian Clauss
ff20316ec9 GitHub Action to lint Python code 2025-01-08 14:48:16 +01:00
11 changed files with 151 additions and 47 deletions

11
.github/workflows/lint_python.yml vendored Normal file
View File

@ -0,0 +1,11 @@
# https://docs.github.com/en/actions
# https://docs.github.com/en/actions/use-cases-and-examples/building-and-testing/building-and-testing-python
# https://docs.astral.sh/ruff
name: lint_python
on: [pull_request, push]
jobs:
lint_python:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: astral-sh/ruff-action@v3

View File

@ -3386,30 +3386,6 @@ With this definition, the above assertion will give a better message:
Actual: 27 (the remainder is 6) Actual: 27 (the remainder is 6)
``` ```
#### Using EXPECT_ Statements in Matchers
You can also use `EXPECT_...` statements inside custom matcher definitions. In
many cases, this allows you to write your matcher more concisely while still
providing an informative error message. For example:
```cpp
MATCHER(IsDivisibleBy7, "") {
const auto remainder = arg % 7;
EXPECT_EQ(remainder, 0);
return true;
}
```
If you write a test that includes the line `EXPECT_THAT(27, IsDivisibleBy7());`,
you will get an error something like the following:
```shell
Expected equality of these values:
remainder
Which is: 6
0
```
#### `MatchAndExplain` #### `MatchAndExplain`
You should let `MatchAndExplain()` print *any additional information* that can You should let `MatchAndExplain()` print *any additional information* that can
@ -3429,6 +3405,66 @@ the value of `(arg % 7) == 0` can be implicitly converted to a `bool`. In the
`arg_type` will be `int`; if it takes an `unsigned long`, `arg_type` will be `arg_type` will be `int`; if it takes an `unsigned long`, `arg_type` will be
`unsigned long`; and so on. `unsigned long`; and so on.
#### Anti-pattern: Using EXPECT_ Statements in Matchers
Using `EXPECT_...` statements inside custom matcher definitions is an
**anti-pattern** and should be avoided.
While it might appear to write matchers more concisely and generate informative
messages, this pattern has critical issues:
1. **Negation Breakage (`Not`):** If wrapped in `Not(IsDivisibleBy7())`,
evaluating it still triggers the internal `EXPECT_EQ`, registering a test
failure on the runner even when the overall assertion is expected to
succeed.
2. **Composition / Container Breakage (`AnyOf`, `AllOf`, `Contains`):** When
composed or used inside container matchers, elements that are expected
mismatches will trigger the internal `EXPECT_` and register spurious
failures.
3. **ASSERT_* compilation errors:** `ASSERT_*` macros use `return;` to abort
from a void function. Since matchers return `bool`, using `ASSERT_` inside
them triggers a compilation error.
4. **Purity Violations:** Matchers must be functionally pure (side-effect
free), whereas registering global failures is a major side effect.
5. **Line Number Confusion:** Failure reports point to the matcher's definition
line rather than the calling `EXPECT_THAT`
line.
##### The Anti-Pattern
```cpp
// Anti-pattern: Do not do this!
MATCHER(IsDivisibleBy7, "") {
const auto remainder = arg % 7;
EXPECT_EQ(remainder, 0); // Spurious failures if negated/composed!
return true;
}
```
##### The Correct Solution
To write concise matchers that delegate to other matchers and safely propagate
the mismatch explanation, use **`::testing::ExplainMatchResult`** instead,
passing it the sub-matcher, the value to check, and the `result_listener`:
```cpp
MATCHER(IsDivisibleBy7, "") {
const auto remainder = arg % 7;
return ::testing::ExplainMatchResult(::testing::Eq(0), remainder,
result_listener);
}
```
If you write a test that includes the line:
```cpp
EXPECT_THAT(28, Not(IsDivisibleBy7()));
```
it will correctly report the mismatch, properly point to the `EXPECT_THAT` line
number, and support negation (`Not`) and composition (`AllOf`, `AnyOf`, etc.)
without registering spurious failures.
### Writing New Parameterized Matchers Quickly ### Writing New Parameterized Matchers Quickly
Sometimes you'll want to define a matcher that has parameters. For that you can Sometimes you'll want to define a matcher that has parameters. For that you can

View File

@ -1957,6 +1957,11 @@ struct SignatureOf<R(Args...)> {
using type = R(Args...); using type = R(Args...);
}; };
template <typename R, typename... Args>
struct SignatureOf<R(Args...) const> {
using type = R(Args...);
};
template <template <typename> class C, typename F> template <template <typename> class C, typename F>
struct SignatureOf<C<F>, struct SignatureOf<C<F>,
typename std::enable_if<std::is_function<F>::value>::type> typename std::enable_if<std::is_function<F>::value>::type>

View File

@ -942,6 +942,15 @@ static constexpr bool IsMockFunctionTemplateArgumentDeducedTo(
} // namespace } // namespace
// Like std::add_const, but for function types.
template <typename F>
struct AddConstToFunction;
template <typename R, typename... Args>
struct AddConstToFunction<R(Args...)> {
using type = R(Args...) const;
};
template <typename F> template <typename F>
class MockMethodMockFunctionSignatureTest : public Test {}; class MockMethodMockFunctionSignatureTest : public Test {};
@ -953,25 +962,69 @@ TYPED_TEST_SUITE(MockMethodMockFunctionSignatureTest,
TYPED_TEST(MockMethodMockFunctionSignatureTest, TYPED_TEST(MockMethodMockFunctionSignatureTest,
IsMockFunctionTemplateArgumentDeducedForRawSignature) { IsMockFunctionTemplateArgumentDeducedForRawSignature) {
using Argument = TypeParam; // Non-const
MockFunction<Argument> foo; {
EXPECT_TRUE(IsMockFunctionTemplateArgumentDeducedTo<TypeParam>(foo)); using Argument = TypeParam;
MockFunction<Argument> foo;
EXPECT_TRUE(IsMockFunctionTemplateArgumentDeducedTo<TypeParam>(foo));
}
// Const
{
using Argument = typename AddConstToFunction<TypeParam>::type;
MockFunction<Argument> foo;
EXPECT_TRUE(IsMockFunctionTemplateArgumentDeducedTo<TypeParam>(foo));
}
} }
TYPED_TEST(MockMethodMockFunctionSignatureTest, TYPED_TEST(MockMethodMockFunctionSignatureTest,
IsMockFunctionTemplateArgumentDeducedForStdFunction) { IsMockFunctionTemplateArgumentDeducedForStdFunction) {
using Argument = std::function<TypeParam>; // Non-const
MockFunction<Argument> foo; {
EXPECT_TRUE(IsMockFunctionTemplateArgumentDeducedTo<TypeParam>(foo)); using Argument = std::function<TypeParam>;
MockFunction<Argument> foo;
EXPECT_TRUE(IsMockFunctionTemplateArgumentDeducedTo<TypeParam>(foo));
}
// As of 2026-05 MSVC doesn't know how to deal with this, providing pages of
// inscrutable errors about std::_Get_function_impl. But this is fine, since
// std::function<R(Args...) const> doesn't apply the const qualifier correctly
// anyway.
#if !defined(_MSC_VER)
// Const
{
using Argument =
std::function<typename AddConstToFunction<TypeParam>::type>;
MockFunction<Argument> foo;
EXPECT_TRUE(IsMockFunctionTemplateArgumentDeducedTo<TypeParam>(foo));
}
#endif
} }
TYPED_TEST( TYPED_TEST(
MockMethodMockFunctionSignatureTest, MockMethodMockFunctionSignatureTest,
IsMockFunctionCallMethodSignatureTheSameForRawSignatureAndStdFunction) { IsMockFunctionCallMethodSignatureTheSameForRawSignatureAndStdFunction) {
using ForRawSignature = decltype(&MockFunction<TypeParam>::Call); // Non-const
using ForStdFunction = {
decltype(&MockFunction<std::function<TypeParam>>::Call); using ForRawSignature = decltype(&MockFunction<TypeParam>::Call);
EXPECT_TRUE((std::is_same<ForRawSignature, ForStdFunction>::value)); using ForStdFunction =
decltype(&MockFunction<std::function<TypeParam>>::Call);
EXPECT_TRUE((std::is_same<ForRawSignature, ForStdFunction>::value));
}
// Const
{
using ConstTypeParam = typename AddConstToFunction<TypeParam>::type;
using ForRawSignature = decltype(&MockFunction<ConstTypeParam>::Call);
using ForStdFunction =
decltype(&MockFunction<std::function<ConstTypeParam>>::Call);
EXPECT_TRUE((std::is_same<ForRawSignature, ForStdFunction>::value));
}
} }
template <typename F> template <typename F>

View File

@ -129,7 +129,7 @@ class GTEST_API_ Message {
int>::type = 0 int>::type = 0
#endif // GTEST_HAS_ABSL #endif // GTEST_HAS_ABSL
> >
inline Message& operator<<(const T& val) { Message& operator<<(const T& val) {
// Some libraries overload << for STL containers. These // Some libraries overload << for STL containers. These
// overloads are defined in the global namespace instead of ::std. // overloads are defined in the global namespace instead of ::std.
// //
@ -155,7 +155,7 @@ class GTEST_API_ Message {
template <typename T, template <typename T,
typename std::enable_if<absl::HasAbslStringify<T>::value, // NOLINT typename std::enable_if<absl::HasAbslStringify<T>::value, // NOLINT
int>::type = 0> int>::type = 0>
inline Message& operator<<(const T& val) { Message& operator<<(const T& val) {
// ::operator<< is needed here for a similar reason as with the non-Abseil // ::operator<< is needed here for a similar reason as with the non-Abseil
// version above // version above
using ::operator<<; using ::operator<<;

View File

@ -229,7 +229,8 @@ GTEST_API_ bool ExitedUnsuccessfully(int exit_status);
goto GTEST_CONCAT_TOKEN_(gtest_label_, __LINE__); \ goto GTEST_CONCAT_TOKEN_(gtest_label_, __LINE__); \
} \ } \
if (gtest_dt != nullptr) { \ if (gtest_dt != nullptr) { \
std::unique_ptr< ::testing::internal::DeathTest> gtest_dt_ptr(gtest_dt); \ const std::unique_ptr< ::testing::internal::DeathTest> gtest_dt_ptr( \
gtest_dt); \
switch (gtest_dt->AssumeRole()) { \ switch (gtest_dt->AssumeRole()) { \
case ::testing::internal::DeathTest::OVERSEE_TEST: \ case ::testing::internal::DeathTest::OVERSEE_TEST: \
if (!gtest_dt->Passed(predicate(gtest_dt->Wait()))) { \ if (!gtest_dt->Passed(predicate(gtest_dt->Wait()))) { \

View File

@ -714,7 +714,7 @@ class GTestFilterUnitTest(gtest_test_utils.TestCase):
def testDisabledBanner(self): def testDisabledBanner(self):
"""Tests that the disabled banner prints only tests that match filter.""" """Tests that the disabled banner prints only tests that match filter."""
make_filter = lambda s: ['--%s=%s' % (FILTER_FLAG, s)] make_filter = lambda s: ['--%s=%s' % (FILTER_FLAG, s)] # noqa: E731
banners = RunAndExtractDisabledBannerList(make_filter('*')) banners = RunAndExtractDisabledBannerList(make_filter('*'))
self.AssertSetEqual( self.AssertSetEqual(

View File

@ -119,7 +119,7 @@ def GetTestCases(tests):
test_cases = [] test_cases = []
for test in tests: for test in tests:
test_case = test.split('.')[0] test_case = test.split('.')[0]
if not test_case in test_cases: if test_case not in test_cases:
test_cases.append(test_case) test_cases.append(test_case)
return test_cases return test_cases

View File

@ -31,20 +31,18 @@
# Suppresses the 'Import not at the top of the file' lint complaint. # Suppresses the 'Import not at the top of the file' lint complaint.
# pylint: disable=g-import-not-at-top # pylint: disable=g-import-not-at-top
import atexit
import os import os
import shutil
import subprocess import subprocess
import sys import sys
import tempfile
import unittest as _test_module
IS_WINDOWS = os.name == 'nt' IS_WINDOWS = os.name == 'nt'
IS_CYGWIN = os.name == 'posix' and 'CYGWIN' in os.uname()[0] IS_CYGWIN = os.name == 'posix' and 'CYGWIN' in os.uname()[0]
IS_OS2 = os.name == 'os2' IS_OS2 = os.name == 'os2'
import atexit
import shutil
import tempfile
import unittest as _test_module
# pylint: enable=g-import-not-at-top
GTEST_OUTPUT_VAR_NAME = 'GTEST_OUTPUT' GTEST_OUTPUT_VAR_NAME = 'GTEST_OUTPUT'
# The environment variable for specifying the path to the premature-exit file. # The environment variable for specifying the path to the premature-exit file.

View File

@ -32,7 +32,7 @@
"""Unit test for the gtest_xml_output module.""" """Unit test for the gtest_xml_output module."""
import os import os
from xml.dom import minidom, Node from xml.dom import minidom
from googletest.test import gtest_test_utils from googletest.test import gtest_test_utils
from googletest.test import gtest_xml_test_utils from googletest.test import gtest_xml_test_utils

View File

@ -30,7 +30,7 @@
"""Unit test utilities for gtest_xml_output""" """Unit test utilities for gtest_xml_output"""
import re import re
from xml.dom import minidom, Node from xml.dom import Node
from googletest.test import gtest_test_utils from googletest.test import gtest_test_utils
GTEST_DEFAULT_OUTPUT_FILE = 'test_detail.xml' GTEST_DEFAULT_OUTPUT_FILE = 'test_detail.xml'