diff --git a/include/gmock/gmock-generated-actions.h b/include/gmock/gmock-generated-actions.h index f511bc8b..f8ffc509 100644 --- a/include/gmock/gmock-generated-actions.h +++ b/include/gmock/gmock-generated-actions.h @@ -1347,7 +1347,7 @@ DoAll(Action1 a1, Action2 a2, Action3 a3, Action4 a4, Action5 a5, Action6 a6, GMOCK_INTERNAL_DECL_TYPE_##value_params>\ class GMOCK_ACTION_CLASS_(name, value_params) {\ public:\ - GMOCK_ACTION_CLASS_(name, value_params)\ + explicit GMOCK_ACTION_CLASS_(name, value_params)\ GMOCK_INTERNAL_INIT_##value_params {}\ template \ class gmock_Impl : public ::testing::ActionInterface {\ @@ -1455,7 +1455,7 @@ DoAll(Action1 a1, Action2 a2, Action3 a3, Action4 a4, Action5 a5, Action6 a6, template \ class name##ActionP {\ public:\ - name##ActionP(p0##_type gmock_p0) : p0(gmock_p0) {}\ + explicit name##ActionP(p0##_type gmock_p0) : p0(gmock_p0) {}\ template \ class gmock_Impl : public ::testing::ActionInterface {\ public:\ diff --git a/include/gmock/gmock-generated-actions.h.pump b/include/gmock/gmock-generated-actions.h.pump index 9fba5988..9197e873 100644 --- a/include/gmock/gmock-generated-actions.h.pump +++ b/include/gmock/gmock-generated-actions.h.pump @@ -606,7 +606,7 @@ $range k 0..n-1 GMOCK_INTERNAL_DECL_TYPE_##value_params>\ class GMOCK_ACTION_CLASS_(name, value_params) {\ public:\ - GMOCK_ACTION_CLASS_(name, value_params)\ + explicit GMOCK_ACTION_CLASS_(name, value_params)\ GMOCK_INTERNAL_INIT_##value_params {}\ template \ class gmock_Impl : public ::testing::ActionInterface {\ @@ -694,7 +694,7 @@ $var macro_name = [[$if i==0 [[ACTION]] $elif i==1 [[ACTION_P]] #define $macro_name(name$for j [[, p$j]])\$template class $class_name {\ public:\ - $class_name($ctor_param_list)$inits {}\ + [[$if i==1 [[explicit ]]]]$class_name($ctor_param_list)$inits {}\ template \ class gmock_Impl : public ::testing::ActionInterface {\ public:\ diff --git a/include/gmock/gmock-generated-matchers.h b/include/gmock/gmock-generated-matchers.h index 967fcf9f..57056fd9 100644 --- a/include/gmock/gmock-generated-matchers.h +++ b/include/gmock/gmock-generated-matchers.h @@ -1460,7 +1460,7 @@ AnyOf(M1 m1, M2 m2, M3 m3, M4 m4, M5 m5, M6 m6, M7 m7, M8 m8, M9 m9, M10 m10) { return ::testing::Matcher(\ new gmock_Impl(p0));\ }\ - name##MatcherP(p0##_type gmock_p0) : p0(gmock_p0) {\ + explicit name##MatcherP(p0##_type gmock_p0) : p0(gmock_p0) {\ }\ p0##_type p0;\ private:\ diff --git a/include/gmock/gmock-generated-matchers.h.pump b/include/gmock/gmock-generated-matchers.h.pump index 4b618a43..de30c2c9 100644 --- a/include/gmock/gmock-generated-matchers.h.pump +++ b/include/gmock/gmock-generated-matchers.h.pump @@ -653,7 +653,7 @@ $var param_field_decls2 = [[$for j return ::testing::Matcher(\ new gmock_Impl($params));\ }\ - $class_name($ctor_param_list)$inits {\ + [[$if i==1 [[explicit ]]]]$class_name($ctor_param_list)$inits {\ }\$param_field_decls2 private:\ GTEST_DISALLOW_ASSIGN_($class_name);\ diff --git a/include/gmock/gmock-matchers.h b/include/gmock/gmock-matchers.h index 086cac39..bd90d81f 100644 --- a/include/gmock/gmock-matchers.h +++ b/include/gmock/gmock-matchers.h @@ -1997,6 +1997,83 @@ class PointeeMatcher { GTEST_DISALLOW_ASSIGN_(PointeeMatcher); }; +// Implements the WhenDynamicCastTo(m) matcher that matches a pointer or +// reference that matches inner_matcher when dynamic_cast is applied. +// The result of dynamic_cast is forwarded to the inner matcher. +// If To is a pointer and the cast fails, the inner matcher will receive NULL. +// If To is a reference and the cast fails, this matcher returns false +// immediately. +template +class WhenDynamicCastToMatcherBase { + public: + explicit WhenDynamicCastToMatcherBase(const Matcher& matcher) + : matcher_(matcher) {} + + void DescribeTo(::std::ostream* os) const { + GetCastTypeDescription(os); + matcher_.DescribeTo(os); + } + + void DescribeNegationTo(::std::ostream* os) const { + GetCastTypeDescription(os); + matcher_.DescribeNegationTo(os); + } + + protected: + const Matcher matcher_; + + static string GetToName() { +#if GTEST_HAS_RTTI + return GetTypeName(); +#else // GTEST_HAS_RTTI + return "the target type"; +#endif // GTEST_HAS_RTTI + } + + private: + static void GetCastTypeDescription(::std::ostream* os) { + *os << "when dynamic_cast to " << GetToName() << ", "; + } + + GTEST_DISALLOW_ASSIGN_(WhenDynamicCastToMatcherBase); +}; + +// Primary template. +// To is a pointer. Cast and forward the result. +template +class WhenDynamicCastToMatcher : public WhenDynamicCastToMatcherBase { + public: + explicit WhenDynamicCastToMatcher(const Matcher& matcher) + : WhenDynamicCastToMatcherBase(matcher) {} + + template + bool MatchAndExplain(From from, MatchResultListener* listener) const { + // TODO(sbenza): Add more detail on failures. ie did the dyn_cast fail? + To to = dynamic_cast(from); + return MatchPrintAndExplain(to, this->matcher_, listener); + } +}; + +// Specialize for references. +// In this case we return false if the dynamic_cast fails. +template +class WhenDynamicCastToMatcher : public WhenDynamicCastToMatcherBase { + public: + explicit WhenDynamicCastToMatcher(const Matcher& matcher) + : WhenDynamicCastToMatcherBase(matcher) {} + + template + bool MatchAndExplain(From& from, MatchResultListener* listener) const { + // We don't want an std::bad_cast here, so do the cast with pointers. + To* to = dynamic_cast(&from); + if (to == NULL) { + *listener << "which cannot be dynamic_cast to " << this->GetToName(); + return false; + } + return MatchPrintAndExplain(*to, this->matcher_, listener); + } +}; + // Implements the Field() matcher for matching a field (i.e. member // variable) of an object. template @@ -3612,6 +3689,19 @@ inline internal::PointeeMatcher Pointee( return internal::PointeeMatcher(inner_matcher); } +// Creates a matcher that matches a pointer or reference that matches +// inner_matcher when dynamic_cast is applied. +// The result of dynamic_cast is forwarded to the inner matcher. +// If To is a pointer and the cast fails, the inner matcher will receive NULL. +// If To is a reference and the cast fails, this matcher returns false +// immediately. +template +inline PolymorphicMatcher > +WhenDynamicCastTo(const Matcher& inner_matcher) { + return MakePolymorphicMatcher( + internal::WhenDynamicCastToMatcher(inner_matcher)); +} + // Creates a matcher that matches an object whose given field matches // 'matcher'. For example, // Field(&Foo::number, Ge(5)) @@ -4050,3 +4140,4 @@ inline InnerMatcher AllArgs(const InnerMatcher& matcher) { return matcher; } } // namespace testing #endif // GMOCK_INCLUDE_GMOCK_GMOCK_MATCHERS_H_ + diff --git a/include/gmock/internal/gmock-internal-utils.h b/include/gmock/internal/gmock-internal-utils.h index 6110dd7b..e2ddb05c 100644 --- a/include/gmock/internal/gmock-internal-utils.h +++ b/include/gmock/internal/gmock-internal-utils.h @@ -447,16 +447,17 @@ class StlContainerView { // ConstReference(const char * (&)[4])') // (and though the N parameter type is mismatched in the above explicit // conversion of it doesn't help - only the conversion of the array). - return type(const_cast(&array[0]), N, kReference); + return type(const_cast(&array[0]), N, + RelationToSourceReference()); #else - return type(array, N, kReference); + return type(array, N, RelationToSourceReference()); #endif // GTEST_OS_SYMBIAN } static type Copy(const Element (&array)[N]) { #if GTEST_OS_SYMBIAN - return type(const_cast(&array[0]), N, kCopy); + return type(const_cast(&array[0]), N, RelationToSourceCopy()); #else - return type(array, N, kCopy); + return type(array, N, RelationToSourceCopy()); #endif // GTEST_OS_SYMBIAN } }; @@ -473,10 +474,10 @@ class StlContainerView< ::testing::tuple > { static const_reference ConstReference( const ::testing::tuple& array) { - return type(get<0>(array), get<1>(array), kReference); + return type(get<0>(array), get<1>(array), RelationToSourceReference()); } static type Copy(const ::testing::tuple& array) { - return type(get<0>(array), get<1>(array), kCopy); + return type(get<0>(array), get<1>(array), RelationToSourceCopy()); } }; @@ -507,3 +508,4 @@ struct BooleanConstant {}; } // namespace testing #endif // GMOCK_INCLUDE_GMOCK_INTERNAL_GMOCK_INTERNAL_UTILS_H_ + diff --git a/test/gmock-generated-actions_test.cc b/test/gmock-generated-actions_test.cc index 784c59d1..c2d2a0a8 100644 --- a/test/gmock-generated-actions_test.cc +++ b/test/gmock-generated-actions_test.cc @@ -900,9 +900,9 @@ template // pattern requires the user to use it directly. ConcatImplActionP3 Concat(const std::string& a, T1 b, T2 c) { - GTEST_INTENTIONAL_CONST_COND_PUSH_ + GTEST_INTENTIONAL_CONST_COND_PUSH_() if (true) { - GTEST_INTENTIONAL_CONST_COND_POP_ + GTEST_INTENTIONAL_CONST_COND_POP_() // This branch verifies that ConcatImpl() can be invoked without // explicit template arguments. return ConcatImpl(a, b, c); diff --git a/test/gmock-internal-utils_test.cc b/test/gmock-internal-utils_test.cc index e5e842a1..95a7dc39 100644 --- a/test/gmock-internal-utils_test.cc +++ b/test/gmock-internal-utils_test.cc @@ -247,9 +247,9 @@ TEST(LosslessArithmeticConvertibleTest, FloatingPointToFloatingPoint) { // Larger size => smaller size is not fine. EXPECT_FALSE((LosslessArithmeticConvertible::value)); - GTEST_INTENTIONAL_CONST_COND_PUSH_ + GTEST_INTENTIONAL_CONST_COND_PUSH_() if (sizeof(double) == sizeof(long double)) { // NOLINT - GTEST_INTENTIONAL_CONST_COND_POP_ + GTEST_INTENTIONAL_CONST_COND_POP_() // In some implementations (e.g. MSVC), double and long double // have the same size. EXPECT_TRUE((LosslessArithmeticConvertible::value)); diff --git a/test/gmock-matchers_test.cc b/test/gmock-matchers_test.cc index 0f6bcb29..ea0153ea 100644 --- a/test/gmock-matchers_test.cc +++ b/test/gmock-matchers_test.cc @@ -33,10 +33,6 @@ // // This file tests some commonly used argument matchers. -// windows.h defines macros which conflict with standard identifiers used in -// this test. Defining this symbol prevents windows.h from doing that. -#define NOMINMAX - #include "gmock/gmock-matchers.h" #include "gmock/gmock-more-matchers.h" @@ -657,8 +653,21 @@ TEST(MatcherCastTest, ValueIsNotCopied) { EXPECT_TRUE(m.Matches(n)); } -class Base {}; -class Derived : public Base {}; +class Base { + public: + virtual ~Base() {} + Base() {} + private: + GTEST_DISALLOW_COPY_AND_ASSIGN_(Base); +}; + +class Derived : public Base { + public: + Derived() : Base() {} + int i; +}; + +class OtherDerived : public Base {}; // Tests that SafeMatcherCast(m) works when m is a polymorphic matcher. TEST(SafeMatcherCastTest, FromPolymorphicMatcher) { @@ -3135,6 +3144,107 @@ TEST(PointeeTest, ReferenceToNonConstRawPointer) { EXPECT_FALSE(m.Matches(p)); } + +MATCHER_P(FieldIIs, inner_matcher, "") { + return ExplainMatchResult(inner_matcher, arg.i, result_listener); +} + +TEST(WhenDynamicCastToTest, SameType) { + Derived derived; + derived.i = 4; + + // Right type. A pointer is passed down. + Base* as_base_ptr = &derived; + EXPECT_THAT(as_base_ptr, WhenDynamicCastTo(Not(IsNull()))); + EXPECT_THAT(as_base_ptr, WhenDynamicCastTo(Pointee(FieldIIs(4)))); + EXPECT_THAT(as_base_ptr, + Not(WhenDynamicCastTo(Pointee(FieldIIs(5))))); +} + +TEST(WhenDynamicCastToTest, WrongTypes) { + Base base; + Derived derived; + OtherDerived other_derived; + + // Wrong types. NULL is passed. + EXPECT_THAT(&base, Not(WhenDynamicCastTo(Pointee(_)))); + EXPECT_THAT(&base, WhenDynamicCastTo(IsNull())); + Base* as_base_ptr = &derived; + EXPECT_THAT(as_base_ptr, Not(WhenDynamicCastTo(Pointee(_)))); + EXPECT_THAT(as_base_ptr, WhenDynamicCastTo(IsNull())); + as_base_ptr = &other_derived; + EXPECT_THAT(as_base_ptr, Not(WhenDynamicCastTo(Pointee(_)))); + EXPECT_THAT(as_base_ptr, WhenDynamicCastTo(IsNull())); +} + +TEST(WhenDynamicCastToTest, AlreadyNull) { + // Already NULL. + Base* as_base_ptr = NULL; + EXPECT_THAT(as_base_ptr, WhenDynamicCastTo(IsNull())); +} + +struct AmbiguousCastTypes { + class VirtualDerived : public virtual Base {}; + class DerivedSub1 : public VirtualDerived {}; + class DerivedSub2 : public VirtualDerived {}; + class ManyDerivedInHierarchy : public DerivedSub1, public DerivedSub2 {}; +}; + +TEST(WhenDynamicCastToTest, AmbiguousCast) { + AmbiguousCastTypes::DerivedSub1 sub1; + AmbiguousCastTypes::ManyDerivedInHierarchy many_derived; + // Multiply derived from Base. dynamic_cast<> returns NULL. + Base* as_base_ptr = + static_cast(&many_derived); + EXPECT_THAT(as_base_ptr, + WhenDynamicCastTo(IsNull())); + as_base_ptr = &sub1; + EXPECT_THAT( + as_base_ptr, + WhenDynamicCastTo(Not(IsNull()))); +} + +TEST(WhenDynamicCastToTest, Describe) { + Matcher matcher = WhenDynamicCastTo(Pointee(_)); +#if GTEST_HAS_RTTI + const string prefix = + "when dynamic_cast to " + internal::GetTypeName() + ", "; +#else // GTEST_HAS_RTTI + const string prefix = "when dynamic_cast, "; +#endif // GTEST_HAS_RTTI + EXPECT_EQ(prefix + "points to a value that is anything", Describe(matcher)); + EXPECT_EQ(prefix + "does not point to a value that is anything", + DescribeNegation(matcher)); +} + +TEST(WhenDynamicCastToTest, Explain) { + Matcher matcher = WhenDynamicCastTo(Pointee(_)); + Base* null = NULL; + EXPECT_THAT(Explain(matcher, null), HasSubstr("NULL")); + Derived derived; + EXPECT_TRUE(matcher.Matches(&derived)); + EXPECT_THAT(Explain(matcher, &derived), HasSubstr("which points to ")); + + // With references, the matcher itself can fail. Test for that one. + Matcher ref_matcher = WhenDynamicCastTo(_); + EXPECT_THAT(Explain(ref_matcher, derived), + HasSubstr("which cannot be dynamic_cast")); +} + +TEST(WhenDynamicCastToTest, GoodReference) { + Derived derived; + derived.i = 4; + Base& as_base_ref = derived; + EXPECT_THAT(as_base_ref, WhenDynamicCastTo(FieldIIs(4))); + EXPECT_THAT(as_base_ref, WhenDynamicCastTo(Not(FieldIIs(5)))); +} + +TEST(WhenDynamicCastToTest, BadReference) { + Derived derived; + Base& as_base_ref = derived; + EXPECT_THAT(as_base_ref, Not(WhenDynamicCastTo(_))); +} + // Minimal const-propagating pointer. template class ConstPropagatingPtr { @@ -3210,17 +3320,24 @@ TEST(PointeeTest, AlwaysExplainsPointee) { // An uncopyable class. class Uncopyable { public: + Uncopyable() : value_(-1) {} explicit Uncopyable(int a_value) : value_(a_value) {} int value() const { return value_; } + void set_value(int i) { value_ = i; } + private: - const int value_; + int value_; GTEST_DISALLOW_COPY_AND_ASSIGN_(Uncopyable); }; // Returns true iff x.value() is positive. bool ValueIsPositive(const Uncopyable& x) { return x.value() > 0; } +MATCHER_P(UncopyableIs, inner_matcher, "") { + return ExplainMatchResult(inner_matcher, arg.value(), result_listener); +} + // A user-defined struct for testing Field(). struct AStruct { AStruct() : x(0), y(1.0), z(5), p(NULL) {} @@ -4527,6 +4644,13 @@ TEST(ElemensAreArrayStreamTest, WorksForStreamlike) { EXPECT_THAT(s, Not(ElementsAreArray(expected))); } +TEST(ElementsAreTest, WorksWithUncopyable) { + Uncopyable objs[2]; + objs[0].set_value(-3); + objs[1].set_value(1); + EXPECT_THAT(objs, ElementsAre(UncopyableIs(-3), Truly(ValueIsPositive))); +} + // Tests for UnorderedElementsAreArray() TEST(UnorderedElementsAreArrayTest, SucceedsWhenExpected) { @@ -4609,6 +4733,14 @@ class UnorderedElementsAreTest : public testing::Test { typedef std::vector IntVec; }; +TEST_F(UnorderedElementsAreTest, WorksWithUncopyable) { + Uncopyable objs[2]; + objs[0].set_value(-3); + objs[1].set_value(1); + EXPECT_THAT(objs, + UnorderedElementsAre(Truly(ValueIsPositive), UncopyableIs(-3))); +} + TEST_F(UnorderedElementsAreTest, SucceedsWhenExpected) { const int a[] = { 1, 2, 3 }; std::vector s(a, a + GTEST_ARRAY_SIZE_(a)); @@ -5332,3 +5464,4 @@ TEST(PointwiseTest, AllowsMonomorphicInnerMatcher) { } // namespace gmock_matchers_test } // namespace testing +