|
|
|
|
|
|
|
|
* Trompeloeil C++ mocking framework |
|
|
* Trompeloeil C++ mocking framework |
|
|
* |
|
|
* |
|
|
* Copyright Björn Fahller 2014-2018 |
|
|
* Copyright Björn Fahller 2014-2018 |
|
|
* Copyright (C) 2017 Andrew Paxie |
|
|
|
|
|
|
|
|
* Copyright (C) 2017, 2018 Andrew Paxie |
|
|
* |
|
|
* |
|
|
* Use, modification and distribution is subject to the |
|
|
* Use, modification and distribution is subject to the |
|
|
* Boost Software License, Version 1.0. (See accompanying |
|
|
* Boost Software License, Version 1.0. (See accompanying |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#if defined(__clang__) |
|
|
#if defined(__clang__) |
|
|
|
|
|
|
|
|
#define TROMPELOEIL_CLANG 1 |
|
|
|
|
|
#define TROMPELOEIL_GCC 0 |
|
|
|
|
|
#define TROMPELOEIL_MSVC 0 |
|
|
|
|
|
|
|
|
# define TROMPELOEIL_CLANG 1 |
|
|
|
|
|
# define TROMPELOEIL_GCC 0 |
|
|
|
|
|
# define TROMPELOEIL_MSVC 0 |
|
|
|
|
|
|
|
|
#define TROMPELOEIL_GCC_VERSION 0 |
|
|
|
|
|
|
|
|
# define TROMPELOEIL_CLANG_VERSION \ |
|
|
|
|
|
(__clang_major__ * 10000 + __clang_minor__ * 100 + __clang_patchlevel__) |
|
|
|
|
|
|
|
|
#define TROMPELOEIL_CPLUSPLUS __cplusplus |
|
|
|
|
|
|
|
|
# define TROMPELOEIL_GCC_VERSION 0 |
|
|
|
|
|
|
|
|
|
|
|
# define TROMPELOEIL_CPLUSPLUS __cplusplus |
|
|
|
|
|
|
|
|
#elif defined(__GNUC__) |
|
|
#elif defined(__GNUC__) |
|
|
|
|
|
|
|
|
#define TROMPELOEIL_CLANG 0 |
|
|
|
|
|
#define TROMPELOEIL_GCC 1 |
|
|
|
|
|
#define TROMPELOEIL_MSVC 0 |
|
|
|
|
|
|
|
|
# define TROMPELOEIL_CLANG 0 |
|
|
|
|
|
# define TROMPELOEIL_GCC 1 |
|
|
|
|
|
# define TROMPELOEIL_MSVC 0 |
|
|
|
|
|
|
|
|
#define TROMPELOEIL_GCC_VERSION \ |
|
|
|
|
|
|
|
|
# define TROMPELOEIL_CLANG_VERSION 0 |
|
|
|
|
|
# define TROMPELOEIL_GCC_VERSION \ |
|
|
(__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) |
|
|
(__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) |
|
|
|
|
|
|
|
|
#define TROMPELOEIL_CPLUSPLUS __cplusplus |
|
|
|
|
|
|
|
|
# define TROMPELOEIL_CPLUSPLUS __cplusplus |
|
|
|
|
|
|
|
|
#elif defined(_MSC_VER) |
|
|
#elif defined(_MSC_VER) |
|
|
|
|
|
|
|
|
#define TROMPELOEIL_CLANG 0 |
|
|
|
|
|
#define TROMPELOEIL_GCC 0 |
|
|
|
|
|
#define TROMPELOEIL_MSVC 1 |
|
|
|
|
|
|
|
|
# define TROMPELOEIL_CLANG 0 |
|
|
|
|
|
# define TROMPELOEIL_GCC 0 |
|
|
|
|
|
# define TROMPELOEIL_MSVC 1 |
|
|
|
|
|
|
|
|
#define TROMPELOEIL_GCC_VERSION 0 |
|
|
|
|
|
|
|
|
# define TROMPELOEIL_CLANG_VERSION 0 |
|
|
|
|
|
# define TROMPELOEIL_GCC_VERSION 0 |
|
|
|
|
|
|
|
|
/* MSVC is an amalgam of C++ versions, with no provision to |
|
|
|
|
|
* force C++11 mode. It also has a __cplusplus macro stuck at 199711L. |
|
|
|
|
|
* Assume the C++14 code path. |
|
|
|
|
|
*/ |
|
|
|
|
|
#define TROMPELOEIL_CPLUSPLUS 201401L |
|
|
|
|
|
|
|
|
# if defined(_MSVC_LANG) |
|
|
|
|
|
|
|
|
|
|
|
// Compiler is at least Microsoft Visual Studio 2015 Update 3. |
|
|
|
|
|
# define TROMPELOEIL_CPLUSPLUS _MSVC_LANG |
|
|
|
|
|
|
|
|
|
|
|
# else /* defined(_MSVC_LANG) */ |
|
|
|
|
|
|
|
|
|
|
|
/* |
|
|
|
|
|
* This version of Microsoft Visual C++ is released |
|
|
|
|
|
* in a version of Microsoft Visual Studio between |
|
|
|
|
|
* 2015 RC and less than 2015 Update 3. |
|
|
|
|
|
* |
|
|
|
|
|
* It is an amalgam of C++ versions, with no provision |
|
|
|
|
|
* to specify C++11 mode. |
|
|
|
|
|
* |
|
|
|
|
|
* It also has a __cplusplus macro stuck at 199711L with |
|
|
|
|
|
* no way to change it, such as /Zc:__cplusplus. |
|
|
|
|
|
* |
|
|
|
|
|
* Assume the C++14 code path, but don't promise that it is a |
|
|
|
|
|
* fully conforming implementation of C++14 either. |
|
|
|
|
|
* Hence a value of 201401L, which less than 201402L, |
|
|
|
|
|
* the standards conforming value of __cplusplus. |
|
|
|
|
|
*/ |
|
|
|
|
|
# define TROMPELOEIL_CPLUSPLUS 201401L |
|
|
|
|
|
|
|
|
|
|
|
# endif /* !defined(_MSVC_LANG) */ |
|
|
|
|
|
|
|
|
#endif |
|
|
#endif |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return {}; |
|
|
return {}; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
template <typename ...> |
|
|
|
|
|
struct void_t_ |
|
|
|
|
|
{ |
|
|
|
|
|
using type = void; |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
template <typename ... T> |
|
|
|
|
|
using void_t = typename void_t_<T...>::type; |
|
|
|
|
|
|
|
|
|
|
|
template <template <typename ...> class, typename, typename ...> |
|
|
|
|
|
struct is_detected_{ |
|
|
|
|
|
using type = std::false_type; |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
template <template <typename ...> class D, typename ... Ts> |
|
|
|
|
|
struct is_detected_<D, void_t<D<Ts...>>, Ts...> |
|
|
|
|
|
{ |
|
|
|
|
|
using type = std::true_type; |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
template <template <typename ...> class D, typename ... Ts> |
|
|
|
|
|
using is_detected = typename is_detected_<D, void, Ts...>::type; |
|
|
|
|
|
|
|
|
# if (TROMPELOEIL_CPLUSPLUS == 201103L) |
|
|
# if (TROMPELOEIL_CPLUSPLUS == 201103L) |
|
|
|
|
|
|
|
|
namespace detail |
|
|
namespace detail |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# endif /* !(TROMPELOEIL_CPLUSPLUS == 201103L) */ |
|
|
# endif /* !(TROMPELOEIL_CPLUSPLUS == 201103L) */ |
|
|
|
|
|
|
|
|
|
|
|
# if TROMPELOEIL_CPLUSPLUS >= 201703L |
|
|
|
|
|
# if TROMPELOEIL_CLANG && TROMPELOEIL_CLANG_VERSION >= 60000 |
|
|
|
|
|
|
|
|
|
|
|
// these are mostly added to work around clang++ bugs |
|
|
|
|
|
// https://bugs.llvm.org/show_bug.cgi?id=38033 |
|
|
|
|
|
// https://bugs.llvm.org/show_bug.cgi?id=38010 |
|
|
|
|
|
|
|
|
|
|
|
template <typename T> |
|
|
|
|
|
using move_construct_type = decltype(T(std::declval<T&&>())); |
|
|
|
|
|
|
|
|
|
|
|
template <typename T> |
|
|
|
|
|
using copy_construct_type = decltype(T(std::declval<const T&>())); |
|
|
|
|
|
|
|
|
|
|
|
template <typename T> |
|
|
|
|
|
using can_move_construct = is_detected<move_construct_type, detail::decay_t<T>>; |
|
|
|
|
|
|
|
|
|
|
|
template <typename T> |
|
|
|
|
|
using can_copy_construct = is_detected<copy_construct_type, detail::decay_t<T>>; |
|
|
|
|
|
|
|
|
|
|
|
# else |
|
|
|
|
|
template <typename T> |
|
|
|
|
|
using can_move_construct = std::is_move_constructible<T>; |
|
|
|
|
|
|
|
|
|
|
|
template <typename T> |
|
|
|
|
|
using can_copy_construct = std::is_copy_constructible<T>; |
|
|
|
|
|
# endif |
|
|
|
|
|
|
|
|
|
|
|
template <typename F, typename ... A> |
|
|
|
|
|
using invoke_result_type = decltype(std::declval<F&>()(std::declval<A>()...)); |
|
|
|
|
|
|
|
|
|
|
|
# else |
|
|
|
|
|
|
|
|
|
|
|
template <typename T> |
|
|
|
|
|
using can_move_construct = std::is_move_constructible<T>; |
|
|
|
|
|
|
|
|
|
|
|
template <typename T> |
|
|
|
|
|
using can_copy_construct = std::is_copy_constructible<T>; |
|
|
|
|
|
|
|
|
|
|
|
template <typename F, typename ... A> |
|
|
|
|
|
using invoke_result_type = decltype(std::declval<F&>()(std::declval<A>()...)); |
|
|
|
|
|
|
|
|
|
|
|
# endif |
|
|
|
|
|
|
|
|
class specialized; |
|
|
class specialized; |
|
|
|
|
|
|
|
|
namespace { |
|
|
namespace { |
|
|
|
|
|
|
|
|
unsigned long line, |
|
|
unsigned long line, |
|
|
std::string const &call) = 0; |
|
|
std::string const &call) = 0; |
|
|
protected: |
|
|
protected: |
|
|
tracer() |
|
|
|
|
|
noexcept |
|
|
|
|
|
: previous{set_tracer(this)} {} |
|
|
|
|
|
|
|
|
tracer() = default; |
|
|
tracer(tracer const&) = delete; |
|
|
tracer(tracer const&) = delete; |
|
|
tracer& operator=(tracer const&) = delete; |
|
|
tracer& operator=(tracer const&) = delete; |
|
|
virtual |
|
|
virtual |
|
|
|
|
|
|
|
|
set_tracer(previous); |
|
|
set_tracer(previous); |
|
|
} |
|
|
} |
|
|
private: |
|
|
private: |
|
|
tracer* previous = nullptr; |
|
|
|
|
|
|
|
|
tracer* previous = set_tracer(this); |
|
|
}; |
|
|
}; |
|
|
|
|
|
|
|
|
class stream_tracer : public tracer |
|
|
class stream_tracer : public tracer |
|
|
|
|
|
|
|
|
} |
|
|
} |
|
|
}; |
|
|
}; |
|
|
|
|
|
|
|
|
template <typename ...> |
|
|
|
|
|
struct void_t_ |
|
|
|
|
|
|
|
|
struct matcher { }; |
|
|
|
|
|
|
|
|
|
|
|
struct wildcard : public matcher |
|
|
{ |
|
|
{ |
|
|
using type = void; |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
// This abomination of constructor seems necessary for g++ 4.9 and 5.1 |
|
|
|
|
|
template <typename ... T> |
|
|
|
|
|
constexpr |
|
|
|
|
|
wildcard( |
|
|
|
|
|
T&& ...) |
|
|
|
|
|
noexcept |
|
|
|
|
|
{} |
|
|
|
|
|
|
|
|
template <typename ... T> |
|
|
|
|
|
using void_t = typename void_t_<T...>::type; |
|
|
|
|
|
|
|
|
#if (!TROMPELOEIL_GCC) || \ |
|
|
|
|
|
(TROMPELOEIL_GCC && TROMPELOEIL_GCC_VERSION >= 40900) |
|
|
|
|
|
|
|
|
template <template <typename ...> class, typename, typename ...> |
|
|
|
|
|
struct is_detected_{ |
|
|
|
|
|
using type = std::false_type; |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
// g++ 4.8 gives a "conversion from <T> to <U> is ambiguous" error |
|
|
|
|
|
// if this operator is defined. |
|
|
|
|
|
template <typename T, |
|
|
|
|
|
typename = detail::enable_if_t<!std::is_lvalue_reference<T>::value>> |
|
|
|
|
|
operator T&&() |
|
|
|
|
|
const; |
|
|
|
|
|
|
|
|
template <template <typename ...> class D, typename ... Ts> |
|
|
|
|
|
struct is_detected_<D, void_t<D<Ts...>>, Ts...> |
|
|
|
|
|
{ |
|
|
|
|
|
using type = std::true_type; |
|
|
|
|
|
|
|
|
#endif |
|
|
|
|
|
|
|
|
|
|
|
template < |
|
|
|
|
|
typename T, |
|
|
|
|
|
typename = detail::enable_if_t< |
|
|
|
|
|
can_copy_construct<T>::value |
|
|
|
|
|
|| !can_move_construct<T>::value |
|
|
|
|
|
> |
|
|
|
|
|
> |
|
|
|
|
|
operator T&() |
|
|
|
|
|
const; |
|
|
|
|
|
|
|
|
|
|
|
template <typename T> |
|
|
|
|
|
constexpr |
|
|
|
|
|
bool |
|
|
|
|
|
matches( |
|
|
|
|
|
T const&) |
|
|
|
|
|
const |
|
|
|
|
|
noexcept |
|
|
|
|
|
{ |
|
|
|
|
|
return true; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
friend |
|
|
|
|
|
std::ostream& |
|
|
|
|
|
operator<<( |
|
|
|
|
|
std::ostream& os, |
|
|
|
|
|
wildcard const&) |
|
|
|
|
|
noexcept |
|
|
|
|
|
{ |
|
|
|
|
|
return os << " matching _"; |
|
|
|
|
|
} |
|
|
}; |
|
|
}; |
|
|
|
|
|
|
|
|
template <template <typename ...> class D, typename ... Ts> |
|
|
|
|
|
using is_detected = typename is_detected_<D, void, Ts...>::type; |
|
|
|
|
|
|
|
|
static constexpr wildcard const _{}; |
|
|
|
|
|
|
|
|
struct matcher { }; |
|
|
|
|
|
|
|
|
|
|
|
template <typename T> |
|
|
|
|
|
|
|
|
template <typename T> |
|
|
using matcher_access = decltype(static_cast<matcher*>(std::declval<typename std::add_pointer<T>::type>())); |
|
|
using matcher_access = decltype(static_cast<matcher*>(std::declval<typename std::add_pointer<T>::type>())); |
|
|
|
|
|
|
|
|
template <typename T> |
|
|
template <typename T> |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
template <typename T, |
|
|
template <typename T, |
|
|
typename = decltype(std::declval<T>() == nullptr), |
|
|
typename = decltype(std::declval<T>() == nullptr), |
|
|
typename = detail::enable_if_t<std::is_copy_constructible<T>::value>> |
|
|
|
|
|
|
|
|
typename = detail::enable_if_t<can_copy_construct<T>::value>> |
|
|
operator T&()const; |
|
|
operator T&()const; |
|
|
|
|
|
|
|
|
template <typename T, typename C> |
|
|
template <typename T, typename C> |
|
|
|
|
|
|
|
|
// g++ 4.8 gives a "conversion from <T> to <U> is ambiguous" error |
|
|
// g++ 4.8 gives a "conversion from <T> to <U> is ambiguous" error |
|
|
// if this operator is defined. |
|
|
// if this operator is defined. |
|
|
template <typename V, |
|
|
template <typename V, |
|
|
typename = decltype(std::declval<Pred>()(std::declval<V&&>(), std::declval<T>()...))> |
|
|
|
|
|
|
|
|
typename = detail::enable_if_t<!is_matcher<V>{}>, |
|
|
|
|
|
typename = invoke_result_type<Pred, V&&, T...>> |
|
|
operator V&&() const; |
|
|
operator V&&() const; |
|
|
|
|
|
|
|
|
#endif |
|
|
#endif |
|
|
|
|
|
|
|
|
template <typename V, |
|
|
template <typename V, |
|
|
typename = decltype(std::declval<Pred>()(std::declval<V&>(), std::declval<T>()...))> |
|
|
|
|
|
|
|
|
typename = detail::enable_if_t<!is_matcher<V>{}>, |
|
|
|
|
|
typename = invoke_result_type<Pred, V&, T...>> |
|
|
operator V&() const; |
|
|
operator V&() const; |
|
|
}; |
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
matchers.push_back(m); |
|
|
matchers.push_back(m); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
struct wildcard : public matcher |
|
|
|
|
|
{ |
|
|
|
|
|
// This abomination of constructor seems necessary for g++ 4.9 and 5.1 |
|
|
|
|
|
template <typename ... T> |
|
|
|
|
|
constexpr |
|
|
|
|
|
wildcard( |
|
|
|
|
|
T&& ...) |
|
|
|
|
|
noexcept |
|
|
|
|
|
{} |
|
|
|
|
|
|
|
|
|
|
|
#if (!TROMPELOEIL_GCC) || \ |
|
|
|
|
|
(TROMPELOEIL_GCC && TROMPELOEIL_GCC_VERSION >= 40900) |
|
|
|
|
|
|
|
|
|
|
|
// g++ 4.8 gives a "conversion from <T> to <U> is ambiguous" error |
|
|
|
|
|
// if this operator is defined. |
|
|
|
|
|
template <typename T, |
|
|
|
|
|
typename = detail::enable_if_t<!std::is_lvalue_reference<T>::value>> |
|
|
|
|
|
operator T&&() |
|
|
|
|
|
const; |
|
|
|
|
|
|
|
|
|
|
|
#endif |
|
|
|
|
|
|
|
|
|
|
|
template <typename T, |
|
|
|
|
|
typename = detail::enable_if_t<std::is_copy_constructible<T>::value |
|
|
|
|
|
|| !std::is_move_constructible<T>::value>> |
|
|
|
|
|
operator T&() |
|
|
|
|
|
const; |
|
|
|
|
|
|
|
|
|
|
|
template <typename T> |
|
|
|
|
|
constexpr |
|
|
|
|
|
bool |
|
|
|
|
|
matches( |
|
|
|
|
|
T const&) |
|
|
|
|
|
const |
|
|
|
|
|
noexcept |
|
|
|
|
|
{ |
|
|
|
|
|
return true; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
friend |
|
|
|
|
|
std::ostream& |
|
|
|
|
|
operator<<( |
|
|
|
|
|
std::ostream& os, |
|
|
|
|
|
wildcard const&) |
|
|
|
|
|
noexcept |
|
|
|
|
|
{ |
|
|
|
|
|
return os << " matching _"; |
|
|
|
|
|
} |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
static constexpr wildcard const _{}; |
|
|
|
|
|
|
|
|
|
|
|
template <typename T> |
|
|
template <typename T> |
|
|
void can_match_parameter(T&); |
|
|
void can_match_parameter(T&); |
|
|
|
|
|
|