mirror of
https://github.com/google/googletest.git
synced 2026-06-15 08:26:11 +08:00
Compare commits
7 Commits
963e84cecb
...
9738019c2a
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9738019c2a | ||
|
|
7140cd416c | ||
|
|
a721f1b20c | ||
|
|
8736d2cd5c | ||
|
|
09f45f51fb | ||
|
|
4ac8c39200 | ||
|
|
36bd22a585 |
@ -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
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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<<;
|
||||
|
||||
@ -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:
|
||||
|
||||
@ -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()))) { \
|
||||
|
||||
@ -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())
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user