Skip to content

Commit

Permalink
Made modifiers inheritable
Browse files Browse the repository at this point in the history
* Mock object can be made Strict/Nice/Naggy by inheritance
  • Loading branch information
AKJ7 committed Dec 26, 2024
1 parent e54519b commit 590080e
Show file tree
Hide file tree
Showing 2 changed files with 86 additions and 103 deletions.
154 changes: 51 additions & 103 deletions googlemock/include/gmock/gmock-nice-strict.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,26 +70,11 @@
#include "gmock/internal/gmock-port.h"

namespace testing {
template <class MockClass>
class NiceMock;
template <class MockClass>
class NaggyMock;
template <class MockClass>
class StrictMock;

namespace internal {
template <typename T>
std::true_type StrictnessModifierProbe(const NiceMock<T>&);
template <typename T>
std::true_type StrictnessModifierProbe(const NaggyMock<T>&);
template <typename T>
std::true_type StrictnessModifierProbe(const StrictMock<T>&);
std::false_type StrictnessModifierProbe(...);
template <class StrictNessModifier, class MockClass>
class StrictNessBase;

template <typename T>
constexpr bool HasStrictnessModifier() {
return decltype(StrictnessModifierProbe(std::declval<const T&>()))::value;
}
namespace internal {

// Base classes that register and deregister with testing::Mock to alter the
// default behavior around uninteresting calls. Inheriting from one of these
Expand Down Expand Up @@ -143,61 +128,58 @@ class StrictMockImpl {
}
};

template <typename T>
std::true_type StrictnessModifierProbe(
const StrictNessBase<internal::NiceMockImpl<T>, T>&);
template <typename T>
std::true_type StrictnessModifierProbe(
const StrictNessBase<internal::NaggyMockImpl<T>, T>&);
template <typename T>
std::true_type StrictnessModifierProbe(
const StrictNessBase<internal::StrictMockImpl<T>, T>&);
std::false_type StrictnessModifierProbe(...);

template <typename T>
constexpr bool HasStrictnessModifier() {
return decltype(StrictnessModifierProbe(std::declval<const T&>()))::value;
}

} // namespace internal

template <class MockClass>
class GTEST_INTERNAL_EMPTY_BASE_CLASS NiceMock
: private internal::NiceMockImpl<MockClass>,
public MockClass {
template <class StrictNessModifier, class MockClass>
class GTEST_INTERNAL_EMPTY_BASE_CLASS StrictNessBase
: public StrictNessModifier {
public:
static_assert(!internal::HasStrictnessModifier<MockClass>(),
static_assert(!internal::HasStrictnessModifier<StrictNessModifier>(),
"Can't apply NiceMock to a class hierarchy that already has a "
"strictness modifier. See "
"https://google.github.io/googletest/"
"gmock_cook_book.html#NiceStrictNaggy");
NiceMock() : MockClass() {
static_assert(sizeof(*this) == sizeof(MockClass),
"The impl subclass shouldn't introduce any padding");
}

// Ideally, we would inherit base class's constructors through a using
// declaration, which would preserve their visibility. However, many existing
// tests rely on the fact that current implementation reexports protected
// constructors as public. These tests would need to be cleaned up first.

// Single argument constructor is special-cased so that it can be
// made explicit.
template <typename A>
explicit NiceMock(A&& arg) : MockClass(std::forward<A>(arg)) {
static_assert(sizeof(*this) == sizeof(MockClass),
"The impl subclass shouldn't introduce any padding");
}

template <typename TArg1, typename TArg2, typename... An>
NiceMock(TArg1&& arg1, TArg2&& arg2, An&&... args)
: MockClass(std::forward<TArg1>(arg1), std::forward<TArg2>(arg2),
std::forward<An>(args)...) {
static_assert(sizeof(*this) == sizeof(MockClass),
"The impl subclass shouldn't introduce any padding");
}
StrictNessBase() = default;

private:
NiceMock(const NiceMock&) = delete;
NiceMock& operator=(const NiceMock&) = delete;
StrictNessBase(const StrictNessBase&) = delete;
StrictNessBase& operator=(const StrictNessBase&) = delete;
};

template <class MockClass>
class GTEST_INTERNAL_EMPTY_BASE_CLASS NaggyMock
: private internal::NaggyMockImpl<MockClass>,
public MockClass {
static_assert(!internal::HasStrictnessModifier<MockClass>(),
"Can't apply NaggyMock to a class hierarchy that already has a "
"strictness modifier. See "
"https://google.github.io/googletest/"
"gmock_cook_book.html#NiceStrictNaggy");
using NiceMockable =
StrictNessBase<internal::NiceMockImpl<MockClass>, MockClass>;

template <class MockClass>
using StrictMockable =
StrictNessBase<internal::StrictMockImpl<MockClass>, MockClass>;

template <class MockClass>
using NaggyMockable =
StrictNessBase<internal::NaggyMockImpl<MockClass>, MockClass>;

template <class StrictNessModifier, class MockClass>
class GTEST_INTERNAL_EMPTY_BASE_CLASS StrictNessMockImplBase
: public StrictNessModifier,
public MockClass {
public:
NaggyMock() : MockClass() {
StrictNessMockImplBase() : MockClass() {
static_assert(sizeof(*this) == sizeof(MockClass),
"The impl subclass shouldn't introduce any padding");
}
Expand All @@ -210,65 +192,31 @@ class GTEST_INTERNAL_EMPTY_BASE_CLASS NaggyMock
// Single argument constructor is special-cased so that it can be
// made explicit.
template <typename A>
explicit NaggyMock(A&& arg) : MockClass(std::forward<A>(arg)) {
explicit StrictNessMockImplBase(A&& arg) : MockClass(std::forward<A>(arg)) {
static_assert(sizeof(*this) == sizeof(MockClass),
"The impl subclass shouldn't introduce any padding");
}

template <typename TArg1, typename TArg2, typename... An>
NaggyMock(TArg1&& arg1, TArg2&& arg2, An&&... args)
StrictNessMockImplBase(TArg1&& arg1, TArg2&& arg2, An&&... args)
: MockClass(std::forward<TArg1>(arg1), std::forward<TArg2>(arg2),
std::forward<An>(args)...) {
static_assert(sizeof(*this) == sizeof(MockClass),
"The impl subclass shouldn't introduce any padding");
}

private:
NaggyMock(const NaggyMock&) = delete;
NaggyMock& operator=(const NaggyMock&) = delete;
};

template <class MockClass>
class GTEST_INTERNAL_EMPTY_BASE_CLASS StrictMock
: private internal::StrictMockImpl<MockClass>,
public MockClass {
public:
static_assert(
!internal::HasStrictnessModifier<MockClass>(),
"Can't apply StrictMock to a class hierarchy that already has a "
"strictness modifier. See "
"https://google.github.io/googletest/"
"gmock_cook_book.html#NiceStrictNaggy");
StrictMock() : MockClass() {
static_assert(sizeof(*this) == sizeof(MockClass),
"The impl subclass shouldn't introduce any padding");
}

// Ideally, we would inherit base class's constructors through a using
// declaration, which would preserve their visibility. However, many existing
// tests rely on the fact that current implementation reexports protected
// constructors as public. These tests would need to be cleaned up first.

// Single argument constructor is special-cased so that it can be
// made explicit.
template <typename A>
explicit StrictMock(A&& arg) : MockClass(std::forward<A>(arg)) {
static_assert(sizeof(*this) == sizeof(MockClass),
"The impl subclass shouldn't introduce any padding");
}
using NiceMock =
StrictNessMockImplBase<internal::NiceMockImpl<MockClass>, MockClass>;

template <typename TArg1, typename TArg2, typename... An>
StrictMock(TArg1&& arg1, TArg2&& arg2, An&&... args)
: MockClass(std::forward<TArg1>(arg1), std::forward<TArg2>(arg2),
std::forward<An>(args)...) {
static_assert(sizeof(*this) == sizeof(MockClass),
"The impl subclass shouldn't introduce any padding");
}
template <class MockClass>
using StrictMock =
StrictNessMockImplBase<internal::StrictMockImpl<MockClass>, MockClass>;

private:
StrictMock(const StrictMock&) = delete;
StrictMock& operator=(const StrictMock&) = delete;
};
template <class MockClass>
using NaggyMock =
StrictNessMockImplBase<internal::NaggyMockImpl<MockClass>, MockClass>;

#undef GTEST_INTERNAL_EMPTY_BASE_CLASS

Expand Down
35 changes: 35 additions & 0 deletions googlemock/test/gmock-nice-strict_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,21 @@ class MockBaz {
MockBaz(MoveOnly) {}
};

class NiceMockModifier : public NiceMockable<NiceMockModifier> {
public:
NiceMockModifier() = default;
};

class NaggyMockModifier : public NaggyMockable<NaggyMockModifier> {
public:
NaggyMockModifier() = default;
};

class StrictMockModifier : public StrictMockable<StrictMockModifier> {
public:
StrictMockModifier() = default;
};

#if GTEST_HAS_STREAM_REDIRECTION

// Tests that a raw mock generates warnings for uninteresting calls.
Expand Down Expand Up @@ -324,6 +339,13 @@ TEST(NiceMockTest, IsNaggy_IsNice_IsStrict) {
EXPECT_FALSE(Mock::IsStrict(&nice_foo));
}

TEST(NiceMockTest, IsNaggy_IsNice_IsStrict_Class) {
NiceMockModifier nice_foo;
EXPECT_FALSE(Mock::IsNaggy(&nice_foo));
EXPECT_TRUE(Mock::IsNice(&nice_foo));
EXPECT_FALSE(Mock::IsStrict(&nice_foo));
}

#if GTEST_HAS_STREAM_REDIRECTION

// Tests that a naggy mock generates warnings for uninteresting calls.
Expand Down Expand Up @@ -443,6 +465,13 @@ TEST(NaggyMockTest, IsNaggy_IsNice_IsStrict) {
EXPECT_FALSE(Mock::IsStrict(&naggy_foo));
}

TEST(NaggyMockTest, IsNaggy_IsNice_IsStrict_Class) {
NaggyMockModifier naggy_foo;
EXPECT_TRUE(Mock::IsNaggy(&naggy_foo));
EXPECT_FALSE(Mock::IsNice(&naggy_foo));
EXPECT_FALSE(Mock::IsStrict(&naggy_foo));
}

// Tests that a strict mock allows expected calls.
TEST(StrictMockTest, AllowsExpectedCall) {
StrictMock<MockFoo> strict_foo;
Expand Down Expand Up @@ -537,5 +566,11 @@ TEST(StrictMockTest, IsNaggy_IsNice_IsStrict) {
EXPECT_TRUE(Mock::IsStrict(&strict_foo));
}

TEST(StrictMockTest, IsNaggy_IsNice_IsStrict_Class) {
StrictMockModifier strict_foo;
EXPECT_FALSE(Mock::IsNaggy(&strict_foo));
EXPECT_FALSE(Mock::IsNice(&strict_foo));
EXPECT_TRUE(Mock::IsStrict(&strict_foo));
}
} // namespace gmock_nice_strict_test
} // namespace testing

0 comments on commit 590080e

Please sign in to comment.