Skip to content

Commit

Permalink
Support mocking methods with move-only return types.
Browse files Browse the repository at this point in the history
  • Loading branch information
kosak committed Jan 29, 2014
1 parent b93d0f1 commit b5c8109
Show file tree
Hide file tree
Showing 5 changed files with 208 additions and 44 deletions.
58 changes: 47 additions & 11 deletions include/gmock/gmock-actions.h
Original file line number Diff line number Diff line change
Expand Up @@ -163,18 +163,27 @@ class DefaultValue {
// Sets the default value for type T; requires T to be
// copy-constructable and have a public destructor.
static void Set(T x) {
delete value_;
value_ = new T(x);
delete producer_;
producer_ = new FixedValueProducer(x);
}

// Provides a factory function to be called to generate the default value.
// This method can be used even if T is only move-constructible, but it is not
// limited to that case.
typedef T (*FactoryFunction)();
static void SetFactory(FactoryFunction factory) {
delete producer_;
producer_ = new FactoryValueProducer(factory);
}

// Unsets the default value for type T.
static void Clear() {
delete value_;
value_ = NULL;
delete producer_;
producer_ = NULL;
}

// Returns true iff the user has set the default value for type T.
static bool IsSet() { return value_ != NULL; }
static bool IsSet() { return producer_ != NULL; }

// Returns true if T has a default return value set by the user or there
// exists a built-in default value.
Expand All @@ -183,15 +192,42 @@ class DefaultValue {
}

// Returns the default value for type T if the user has set one;
// otherwise returns the built-in default value if there is one;
// otherwise aborts the process.
// otherwise returns the built-in default value. Requires that Exists()
// is true, which ensures that the return value is well-defined.
static T Get() {
return value_ == NULL ?
internal::BuiltInDefaultValue<T>::Get() : *value_;
return producer_ == NULL ?
internal::BuiltInDefaultValue<T>::Get() : producer_->Produce();
}

private:
static const T* value_;
class ValueProducer {
public:
virtual ~ValueProducer() {}
virtual T Produce() = 0;
};

class FixedValueProducer : public ValueProducer {
public:
explicit FixedValueProducer(T value) : value_(value) {}
virtual T Produce() { return value_; }

private:
const T value_;
GTEST_DISALLOW_COPY_AND_ASSIGN_(FixedValueProducer);
};

class FactoryValueProducer : public ValueProducer {
public:
explicit FactoryValueProducer(FactoryFunction factory)
: factory_(factory) {}
virtual T Produce() { return factory_(); }

private:
const FactoryFunction factory_;
GTEST_DISALLOW_COPY_AND_ASSIGN_(FactoryValueProducer);
};

static ValueProducer* producer_;
};

// This partial specialization allows a user to set default values for
Expand Down Expand Up @@ -241,7 +277,7 @@ class DefaultValue<void> {

// Points to the user-set default value for type T.
template <typename T>
const T* DefaultValue<T>::value_ = NULL;
typename DefaultValue<T>::ValueProducer* DefaultValue<T>::producer_ = NULL;

// Points to the user-set default value for type T&.
template <typename T>
Expand Down
106 changes: 81 additions & 25 deletions include/gmock/gmock-spec-builders.h
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,7 @@ class GTEST_API_ UntypedFunctionMockerBase {
// arguments. This function can be safely called from multiple
// threads concurrently. The caller is responsible for deleting the
// result.
const UntypedActionResultHolderBase* UntypedInvokeWith(
UntypedActionResultHolderBase* UntypedInvokeWith(
const void* untyped_args)
GTEST_LOCK_EXCLUDED_(g_gmock_mutex);

Expand Down Expand Up @@ -1289,6 +1289,58 @@ class MockSpec {
GTEST_DISALLOW_ASSIGN_(MockSpec);
}; // class MockSpec

// Wrapper type for generically holding an ordinary value or lvalue reference.
// If T is not a reference type, it must be copyable or movable.
// ReferenceOrValueWrapper<T> is movable, and will also be copyable unless
// T is a move-only value type (which means that it will always be copyable
// if the current platform does not support move semantics).
//
// The primary template defines handling for values, but function header
// comments describe the contract for the whole template (including
// specializations).
template <typename T>
class ReferenceOrValueWrapper {
public:
// Constructs a wrapper from the given value/reference.
explicit ReferenceOrValueWrapper(T value)
: value_(GTEST_MOVE_(value)) {}

// Unwraps and returns the underlying value/reference, exactly as
// originally passed. The behavior of calling this more than once on
// the same object is unspecified.
T Unwrap() {
return GTEST_MOVE_(value_);
}

// Provides nondestructive access to the underlying value/reference.
// Always returns a const reference (more precisely,
// const RemoveReference<T>&). The behavior of calling this after
// calling Unwrap on the same object is unspecified.
const T& Peek() const {
return value_;
}

private:
T value_;
};

// Specialization for lvalue reference types. See primary template
// for documentation.
template <typename T>
class ReferenceOrValueWrapper<T&> {
public:
// Workaround for debatable pass-by-reference lint warning (c-library-team
// policy precludes NOLINT in this context)
typedef T& reference;
explicit ReferenceOrValueWrapper(reference ref)
: value_ptr_(&ref) {}
T& Unwrap() { return *value_ptr_; }
const T& Peek() const { return *value_ptr_; }

private:
T* value_ptr_;
};

// MSVC warns about using 'this' in base member initializer list, so
// we need to temporarily disable the warning. We have to do it for
// the entire class to suppress the warning, even though it's about
Expand Down Expand Up @@ -1320,23 +1372,16 @@ class UntypedActionResultHolderBase {
template <typename T>
class ActionResultHolder : public UntypedActionResultHolderBase {
public:
explicit ActionResultHolder(T a_value) : value_(a_value) {}

// The compiler-generated copy constructor and assignment operator
// are exactly what we need, so we don't need to define them.

// Returns the held value and deletes this object.
T GetValueAndDelete() const {
T retval(value_);
delete this;
return retval;
// Returns the held value. Must not be called more than once.
T Unwrap() {
return result_.Unwrap();
}

// Prints the held value as an action's result to os.
virtual void PrintAsActionResult(::std::ostream* os) const {
*os << "\n Returns: ";
// T may be a reference type, so we don't use UniversalPrint().
UniversalPrinter<T>::Print(value_, os);
UniversalPrinter<T>::Print(result_.Peek(), os);
}

// Performs the given mock function's default action and returns the
Expand All @@ -1346,8 +1391,8 @@ class ActionResultHolder : public UntypedActionResultHolderBase {
const FunctionMockerBase<F>* func_mocker,
const typename Function<F>::ArgumentTuple& args,
const string& call_description) {
return new ActionResultHolder(
func_mocker->PerformDefaultAction(args, call_description));
return new ActionResultHolder(Wrapper(
func_mocker->PerformDefaultAction(args, call_description)));
}

// Performs the given action and returns the result in a new-ed
Expand All @@ -1356,42 +1401,52 @@ class ActionResultHolder : public UntypedActionResultHolderBase {
static ActionResultHolder*
PerformAction(const Action<F>& action,
const typename Function<F>::ArgumentTuple& args) {
return new ActionResultHolder(action.Perform(args));
return new ActionResultHolder(Wrapper(action.Perform(args)));
}

private:
T value_;
typedef ReferenceOrValueWrapper<T> Wrapper;

// T could be a reference type, so = isn't supported.
GTEST_DISALLOW_ASSIGN_(ActionResultHolder);
explicit ActionResultHolder(Wrapper result)
: result_(GTEST_MOVE_(result)) {}

Wrapper result_;

GTEST_DISALLOW_COPY_AND_ASSIGN_(ActionResultHolder);
};

// Specialization for T = void.
template <>
class ActionResultHolder<void> : public UntypedActionResultHolderBase {
public:
void GetValueAndDelete() const { delete this; }
void Unwrap() { }

virtual void PrintAsActionResult(::std::ostream* /* os */) const {}

// Performs the given mock function's default action and returns NULL;
// Performs the given mock function's default action and returns ownership
// of an empty ActionResultHolder*.
template <typename F>
static ActionResultHolder* PerformDefaultAction(
const FunctionMockerBase<F>* func_mocker,
const typename Function<F>::ArgumentTuple& args,
const string& call_description) {
func_mocker->PerformDefaultAction(args, call_description);
return NULL;
return new ActionResultHolder;
}

// Performs the given action and returns NULL.
// Performs the given action and returns ownership of an empty
// ActionResultHolder*.
template <typename F>
static ActionResultHolder* PerformAction(
const Action<F>& action,
const typename Function<F>::ArgumentTuple& args) {
action.Perform(args);
return NULL;
return new ActionResultHolder;
}

private:
ActionResultHolder() {}
GTEST_DISALLOW_COPY_AND_ASSIGN_(ActionResultHolder);
};

// The base of the function mocker class for the given function type.
Expand Down Expand Up @@ -1526,8 +1581,9 @@ class FunctionMockerBase : public UntypedFunctionMockerBase {
// threads concurrently.
Result InvokeWith(const ArgumentTuple& args)
GTEST_LOCK_EXCLUDED_(g_gmock_mutex) {
return static_cast<const ResultHolder*>(
this->UntypedInvokeWith(&args))->GetValueAndDelete();
scoped_ptr<ResultHolder> holder(
DownCast_<ResultHolder*>(this->UntypedInvokeWith(&args)));
return holder->Unwrap();
}

// Adds and returns a default action spec for this mock function.
Expand Down
23 changes: 18 additions & 5 deletions include/gmock/internal/gmock-internal-utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -361,17 +361,30 @@ template <typename T> struct DecayArray<T[]> {
typedef const T* type;
};

// Invalid<T>() returns an invalid value of type T. This is useful
// Disable MSVC warnings for infinite recursion, since in this case the
// the recursion is unreachable.
#ifdef _MSC_VER
# pragma warning(push)
# pragma warning(disable:4717)
#endif

// Invalid<T>() is usable as an expression of type T, but will terminate
// the program with an assertion failure if actually run. This is useful
// when a value of type T is needed for compilation, but the statement
// will not really be executed (or we don't care if the statement
// crashes).
template <typename T>
inline T Invalid() {
return const_cast<typename remove_reference<T>::type&>(
*static_cast<volatile typename remove_reference<T>::type*>(NULL));
Assert(false, "", -1, "Internal error: attempt to return invalid value");
// This statement is unreachable, and would never terminate even if it
// could be reached. It is provided only to placate compiler warnings
// about missing return statements.
return Invalid<T>();
}
template <>
inline void Invalid<void>() {}

#ifdef _MSC_VER
# pragma warning(pop)
#endif

// Given a raw type (i.e. having no top-level reference or const
// modifier) RawContainer that's either an STL-style container or a
Expand Down
6 changes: 3 additions & 3 deletions src/gmock-spec-builders.cc
Original file line number Diff line number Diff line change
Expand Up @@ -332,7 +332,7 @@ const char* UntypedFunctionMockerBase::Name() const
// Calculates the result of invoking this mock function with the given
// arguments, prints it, and returns it. The caller is responsible
// for deleting the result.
const UntypedActionResultHolderBase*
UntypedActionResultHolderBase*
UntypedFunctionMockerBase::UntypedInvokeWith(const void* const untyped_args)
GTEST_LOCK_EXCLUDED_(g_gmock_mutex) {
if (untyped_expectations_.size() == 0) {
Expand Down Expand Up @@ -370,7 +370,7 @@ UntypedFunctionMockerBase::UntypedInvokeWith(const void* const untyped_args)
this->UntypedDescribeUninterestingCall(untyped_args, &ss);

// Calculates the function result.
const UntypedActionResultHolderBase* const result =
UntypedActionResultHolderBase* const result =
this->UntypedPerformDefaultAction(untyped_args, ss.str());

// Prints the function result.
Expand Down Expand Up @@ -417,7 +417,7 @@ UntypedFunctionMockerBase::UntypedInvokeWith(const void* const untyped_args)
untyped_expectation->DescribeLocationTo(&loc);
}

const UntypedActionResultHolderBase* const result =
UntypedActionResultHolderBase* const result =
untyped_action == NULL ?
this->UntypedPerformDefaultAction(untyped_args, ss.str()) :
this->UntypedPerformAction(untyped_action, untyped_args);
Expand Down
Loading

0 comments on commit b5c8109

Please sign in to comment.