Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Made mock modifiers inheritable #4689

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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