Compare commits

...

7 Commits

Author SHA1 Message Date
Mara Sophie Grosch
9738019c2a
Merge 4ac8c39200d36e332ae3a26373bb7f54059619a4 into 7140cd416cecd7462a8aae488024abeee55598e4 2026-06-11 19:28:19 +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
Mara Sophie Grosch
4ac8c39200 make XmlUnitTestResultPrinter usable without filesystem
Make XmlUnitTestResultPrinter usable when GTEST_HAS_FILE_SYSTEM=0,
though this should probably be another class extending
XmlUnitTestResultPrinter instead - but feels like the preprocessor
feature flag logic matches the existing code base.
2023-10-29 05:52:10 +01:00
Mara Sophie Grosch
36bd22a585 Add public interface for XmlUnitTestResultPrinter
Allowing custom gtest_main implementations to instantiate the
XmlUnitTestResultPrinter with either a given file path or any other kind
of std::ostream to write to.

This is useful for e.g. embedded cases where an XML report is still
wanted, but not file system is available, by instantiating with a
std::stringstream and delivering the data via any custom mean.

Related to #1930
2023-10-29 04:46:17 +01:00
7 changed files with 220 additions and 81 deletions

View File

@ -3386,30 +3386,6 @@ With this definition, the above assertion will give a better message:
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`
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
`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
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...);
};
template <typename R, typename... Args>
struct SignatureOf<R(Args...) const> {
using type = R(Args...);
};
template <template <typename> class C, typename F>
struct SignatureOf<C<F>,
typename std::enable_if<std::is_function<F>::value>::type>

View File

@ -942,6 +942,15 @@ static constexpr bool IsMockFunctionTemplateArgumentDeducedTo(
} // 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>
class MockMethodMockFunctionSignatureTest : public Test {};
@ -953,25 +962,69 @@ TYPED_TEST_SUITE(MockMethodMockFunctionSignatureTest,
TYPED_TEST(MockMethodMockFunctionSignatureTest,
IsMockFunctionTemplateArgumentDeducedForRawSignature) {
using Argument = TypeParam;
MockFunction<Argument> foo;
EXPECT_TRUE(IsMockFunctionTemplateArgumentDeducedTo<TypeParam>(foo));
// Non-const
{
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,
IsMockFunctionTemplateArgumentDeducedForStdFunction) {
using Argument = std::function<TypeParam>;
MockFunction<Argument> foo;
EXPECT_TRUE(IsMockFunctionTemplateArgumentDeducedTo<TypeParam>(foo));
// Non-const
{
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(
MockMethodMockFunctionSignatureTest,
IsMockFunctionCallMethodSignatureTheSameForRawSignatureAndStdFunction) {
using ForRawSignature = decltype(&MockFunction<TypeParam>::Call);
using ForStdFunction =
decltype(&MockFunction<std::function<TypeParam>>::Call);
EXPECT_TRUE((std::is_same<ForRawSignature, ForStdFunction>::value));
// Non-const
{
using ForRawSignature = decltype(&MockFunction<TypeParam>::Call);
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>

View File

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

View File

@ -1023,6 +1023,27 @@ class [[nodiscard]] EmptyTestEventListener : public TestEventListener {
void OnTestProgramEnd(const UnitTest& /*unit_test*/) override {}
};
class XmlUnitTestResultPrinter : public EmptyTestEventListener {
public:
#if GTEST_HAS_FILE_SYSTEM
XmlUnitTestResultPrinter(const char* output_file);
#endif // GTEST_HAS_FILE_SYSTEM
XmlUnitTestResultPrinter(std::ostream* output_stream);
XmlUnitTestResultPrinter(const XmlUnitTestResultPrinter&) = delete;
XmlUnitTestResultPrinter& operator=(const XmlUnitTestResultPrinter&) = delete;
void OnTestIterationEnd(const UnitTest& unit_test, int iteration) override;
void ListTestsMatchingFilter(const std::vector<TestSuite*>& test_suites);
private:
#if GTEST_HAS_FILE_SYSTEM
const std::string output_file_;
#endif // GTEST_HAS_FILE_SYSTEM
std::ostream* output_stream_;
};
// TestEventListeners lets users add listeners to track events in Google Test.
class GTEST_API_ [[nodiscard]] TestEventListeners {
public:

View File

@ -229,7 +229,8 @@ GTEST_API_ bool ExitedUnsuccessfully(int exit_status);
goto GTEST_CONCAT_TOKEN_(gtest_label_, __LINE__); \
} \
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()) { \
case ::testing::internal::DeathTest::OVERSEE_TEST: \
if (!gtest_dt->Passed(predicate(gtest_dt->Wait()))) { \

View File

@ -2427,7 +2427,6 @@ static std::vector<std::string> GetReservedAttributesForElement(
return std::vector<std::string>();
}
#if GTEST_HAS_FILE_SYSTEM
// TODO(jdesprez): Merge the two getReserved attributes once skip is improved
// This function is only used when file systems are enabled.
static std::vector<std::string> GetReservedOutputAttributesForElement(
@ -2444,7 +2443,6 @@ static std::vector<std::string> GetReservedOutputAttributesForElement(
// This code is unreachable but some compilers may not realizes that.
return std::vector<std::string>();
}
#endif
static std::string FormatWordList(const std::vector<std::string>& words) {
Message word_list;
@ -3974,14 +3972,10 @@ void TestEventRepeater::OnTestIterationEnd(const UnitTest& unit_test,
// End TestEventRepeater
#if GTEST_HAS_FILE_SYSTEM
// This class generates an XML output file.
class XmlUnitTestResultPrinter : public EmptyTestEventListener {
public:
explicit XmlUnitTestResultPrinter(const char* output_file);
void OnTestIterationEnd(const UnitTest& unit_test, int iteration) override;
void ListTestsMatchingFilter(const std::vector<TestSuite*>& test_suites);
XmlUnitTestResultPrinter() = delete;
// Prints an XML summary of all unit tests.
static void PrintXmlTestsList(std::ostream* stream,
@ -4064,40 +4058,9 @@ class XmlUnitTestResultPrinter : public EmptyTestEventListener {
const TestResult& result,
const std::string& indent);
// The output file.
const std::string output_file_;
XmlUnitTestResultPrinter(const XmlUnitTestResultPrinter&) = delete;
XmlUnitTestResultPrinter& operator=(const XmlUnitTestResultPrinter&) = delete;
friend ::testing::XmlUnitTestResultPrinter;
};
// Creates a new XmlUnitTestResultPrinter.
XmlUnitTestResultPrinter::XmlUnitTestResultPrinter(const char* output_file)
: output_file_(output_file) {
if (output_file_.empty()) {
GTEST_LOG_(FATAL) << "XML output file may not be null";
}
}
// Called after the unit test ends.
void XmlUnitTestResultPrinter::OnTestIterationEnd(const UnitTest& unit_test,
int /*iteration*/) {
FILE* xmlout = OpenFileForWriting(output_file_);
std::stringstream stream;
PrintXmlUnitTest(&stream, unit_test);
fprintf(xmlout, "%s", StringStreamToString(&stream).c_str());
fclose(xmlout);
}
void XmlUnitTestResultPrinter::ListTestsMatchingFilter(
const std::vector<TestSuite*>& test_suites) {
FILE* xmlout = OpenFileForWriting(output_file_);
std::stringstream stream;
PrintXmlTestsList(&stream, test_suites);
fprintf(xmlout, "%s", StringStreamToString(&stream).c_str());
fclose(xmlout);
}
// Returns an XML-escaped copy of the input string str. If is_attribute
// is true, the text is meant to appear as an attribute value, and
// normalizable whitespace is preserved by replacing it with character
@ -4529,7 +4492,6 @@ void XmlUnitTestResultPrinter::OutputXmlTestProperties(
}
// End XmlUnitTestResultPrinter
#endif // GTEST_HAS_FILE_SYSTEM
#if GTEST_HAS_FILE_SYSTEM
// This class generates an JSON output file.
@ -5273,6 +5235,69 @@ void TestEventListeners::SuppressEventForwarding(bool suppress) {
repeater_->set_forwarding_enabled(!suppress);
}
#if GTEST_HAS_FILE_SYSTEM
// Creates a new XmlUnitTestResultPrinter.
XmlUnitTestResultPrinter::XmlUnitTestResultPrinter(const char* output_file)
: output_file_(output_file) {
if (output_file_.empty()) {
GTEST_LOG_(FATAL) << "XML output file may not be null";
}
}
#endif // GTEST_HAS_FILE_SYSTEM
// Creates a new XmlUnitTestResultPrinter.
XmlUnitTestResultPrinter::XmlUnitTestResultPrinter(std::ostream* output_stream)
: output_stream_(output_stream) {
if (!output_stream->good()) {
GTEST_LOG_(FATAL) << "XML output is not good";
}
}
// Called after the unit test ends.
void XmlUnitTestResultPrinter::OnTestIterationEnd(const UnitTest& unit_test,
int /*iteration*/) {
#if GTEST_HAS_FILE_SYSTEM
FILE* xmlout = 0;
if(!output_stream_) {
xmlout = internal::OpenFileForWriting(output_file_);
output_stream_ = new std::stringstream;
}
#endif // GTEST_HAS_FILE_SYSTEM
internal::XmlUnitTestResultPrinter::PrintXmlUnitTest(output_stream_, unit_test);
#if GTEST_HAS_FILE_SYSTEM
if(xmlout) {
fprintf(xmlout, "%s", internal::StringStreamToString(static_cast<std::stringstream*>(output_stream_)).c_str());
fclose(xmlout);
delete output_stream_;
output_stream_ = 0;
}
#endif // GTEST_HAS_FILE_SYSTEM
}
void XmlUnitTestResultPrinter::ListTestsMatchingFilter(
const std::vector<TestSuite*>& test_suites) {
#if GTEST_HAS_FILE_SYSTEM
FILE* xmlout = 0;
if(!output_stream_) {
xmlout = internal::OpenFileForWriting(output_file_);
output_stream_ = new std::stringstream;
}
#endif // GTEST_HAS_FILE_SYSTEM
internal::XmlUnitTestResultPrinter::PrintXmlTestsList(output_stream_, test_suites);
#if GTEST_HAS_FILE_SYSTEM
if(xmlout) {
fprintf(xmlout, "%s", internal::StringStreamToString(static_cast<std::stringstream*>(output_stream_)).c_str());
fclose(xmlout);
delete output_stream_;
output_stream_ = 0;
}
#endif // GTEST_HAS_FILE_SYSTEM
}
// class UnitTest
// Gets the singleton UnitTest object. The first time this method is
@ -5774,7 +5799,7 @@ void UnitTestImpl::ConfigureXmlOutput() {
const std::string& output_format = UnitTestOptions::GetOutputFormat();
#if GTEST_HAS_FILE_SYSTEM
if (output_format == "xml") {
listeners()->SetDefaultXmlGenerator(new XmlUnitTestResultPrinter(
listeners()->SetDefaultXmlGenerator(new ::testing::XmlUnitTestResultPrinter(
UnitTestOptions::GetAbsolutePathToOutputFile().c_str()));
} else if (output_format == "json") {
listeners()->SetDefaultXmlGenerator(new JsonUnitTestResultPrinter(
@ -6425,9 +6450,7 @@ void UnitTestImpl::ListTestsMatchingFilter() {
OpenFileForWriting(UnitTestOptions::GetAbsolutePathToOutputFile());
std::stringstream stream;
if (output_format == "xml") {
XmlUnitTestResultPrinter(
UnitTestOptions::GetAbsolutePathToOutputFile().c_str())
.PrintXmlTestsList(&stream, test_suites_);
XmlUnitTestResultPrinter::PrintXmlTestsList(&stream, test_suites_);
} else if (output_format == "json") {
JsonUnitTestResultPrinter(
UnitTestOptions::GetAbsolutePathToOutputFile().c_str())