/* * Trompeloeil C++ mocking framework * * Copyright Björn Fahller 2014-2018 * Copyright (C) 2017 Andrew Paxie * * Use, modification and distribution is subject to the * Boost Software License, Version 1.0. (See accompanying * file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) * * Project home: https://github.com/rollbear/trompeloeil */ #ifndef TROMPELOEIL_HPP_ #define TROMPELOEIL_HPP_ // trompe l'oeil noun (Concise Encyclopedia) // Style of representation in which a painted object is intended // to deceive the viewer into believing it is the object itself... // project home: https://github.com/rollbear/trompeloeil // Deficiencies and missing features // * Mocking function templates is not supported // * If a macro kills a kitten, this threatens extinction of all felines! #if defined(_MSC_VER) # define TROMPELOEIL_NORETURN __declspec(noreturn) # if (!defined(__cplusplus) || _MSC_VER < 1900) # error requires C++ in Visual Studio 2015 RC or later # endif #else # define TROMPELOEIL_NORETURN [[noreturn]] # if (!defined(__cplusplus) || __cplusplus < 201103L) # error requires C++11 or higher # endif #endif #if defined(__clang__) #define TROMPELOEIL_CLANG 1 #define TROMPELOEIL_GCC 0 #define TROMPELOEIL_MSVC 0 #define TROMPELOEIL_GCC_VERSION 0 #define TROMPELOEIL_CPLUSPLUS __cplusplus #elif defined(__GNUC__) #define TROMPELOEIL_CLANG 0 #define TROMPELOEIL_GCC 1 #define TROMPELOEIL_MSVC 0 #define TROMPELOEIL_GCC_VERSION \ (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) #define TROMPELOEIL_CPLUSPLUS __cplusplus #elif defined(_MSC_VER) #define TROMPELOEIL_CLANG 0 #define TROMPELOEIL_GCC 0 #define TROMPELOEIL_MSVC 1 #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 #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef TROMPELOEIL_SANITY_CHECKS #include #define TROMPELOEIL_ASSERT(x) assert(x) #else #define TROMPELOEIL_ASSERT(x) do {} while (false) #endif #define TROMPELOEIL_IDENTITY(...) __VA_ARGS__ // work around stupid MS VS2015 RC bug #define TROMPELOEIL_ARG16(_0,_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15, ...) _15 #define TROMPELOEIL_COUNT(...) \ TROMPELOEIL_IDENTITY(TROMPELOEIL_ARG16(__VA_ARGS__, \ 15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0)) #define TROMPELOEIL_CONCAT_(x, y) x ## y #define TROMPELOEIL_CONCAT(x, y) TROMPELOEIL_CONCAT_(x, y) #define TROMPELOEIL_INIT_WITH_STR15(base, x, ...) \ base{#x, x}, TROMPELOEIL_INIT_WITH_STR14(base, __VA_ARGS__) #define TROMPELOEIL_INIT_WITH_STR14(base, x, ...) \ base{#x, x}, TROMPELOEIL_INIT_WITH_STR13(base, __VA_ARGS__) #define TROMPELOEIL_INIT_WITH_STR13(base, x, ...) \ base{#x, x}, TROMPELOEIL_INIT_WITH_STR12(base, __VA_ARGS__) #define TROMPELOEIL_INIT_WITH_STR12(base, x, ...) \ base{#x, x}, TROMPELOEIL_INIT_WITH_STR11(base, __VA_ARGS__) #define TROMPELOEIL_INIT_WITH_STR11(base, x, ...) \ base{#x, x}, TROMPELOEIL_INIT_WITH_STR10(base, __VA_ARGS__) #define TROMPELOEIL_INIT_WITH_STR10(base, x, ...) \ base{#x, x}, TROMPELOEIL_INIT_WITH_STR9(base, __VA_ARGS__) #define TROMPELOEIL_INIT_WITH_STR9(base, x, ...) \ base{#x, x}, TROMPELOEIL_INIT_WITH_STR8(base, __VA_ARGS__) #define TROMPELOEIL_INIT_WITH_STR8(base, x, ...) \ base{#x, x}, TROMPELOEIL_INIT_WITH_STR7(base, __VA_ARGS__) #define TROMPELOEIL_INIT_WITH_STR7(base, x, ...) \ base{#x, x}, TROMPELOEIL_INIT_WITH_STR6(base, __VA_ARGS__) #define TROMPELOEIL_INIT_WITH_STR6(base, x, ...) \ base{#x, x}, TROMPELOEIL_INIT_WITH_STR5(base, __VA_ARGS__) #define TROMPELOEIL_INIT_WITH_STR5(base, x, ...) \ base{#x, x}, TROMPELOEIL_INIT_WITH_STR4(base, __VA_ARGS__) #define TROMPELOEIL_INIT_WITH_STR4(base, x, ...) \ base{#x, x}, TROMPELOEIL_INIT_WITH_STR3(base, __VA_ARGS__) #define TROMPELOEIL_INIT_WITH_STR3(base, x, ...) \ base{#x, x}, TROMPELOEIL_INIT_WITH_STR2(base, __VA_ARGS__) #define TROMPELOEIL_INIT_WITH_STR2(base, x, ...) \ base{#x, x}, TROMPELOEIL_INIT_WITH_STR1(base, __VA_ARGS__) #define TROMPELOEIL_INIT_WITH_STR1(base, x) \ base{#x, x} #define TROMPELOEIL_INIT_WITH_STR0(base) #define TROMPELOEIL_INIT_WITH_STR(base, ...) \ TROMPELOEIL_CONCAT(TROMPELOEIL_INIT_WITH_STR, \ TROMPELOEIL_COUNT(__VA_ARGS__))(base, __VA_ARGS__) #define TROMPELOEIL_PARAM_LIST15(func_type) \ TROMPELOEIL_PARAM_LIST14(func_type), \ ::trompeloeil::param_list_t p15 #define TROMPELOEIL_PARAM_LIST14(func_type) \ TROMPELOEIL_PARAM_LIST13(func_type), \ ::trompeloeil::param_list_t p14 #define TROMPELOEIL_PARAM_LIST13(func_type) \ TROMPELOEIL_PARAM_LIST12(func_type), \ ::trompeloeil::param_list_t p13 #define TROMPELOEIL_PARAM_LIST12(func_type) \ TROMPELOEIL_PARAM_LIST11(func_type), \ ::trompeloeil::param_list_t p12 #define TROMPELOEIL_PARAM_LIST11(func_type) \ TROMPELOEIL_PARAM_LIST10(func_type), \ ::trompeloeil::param_list_t p11 #define TROMPELOEIL_PARAM_LIST10(func_type) \ TROMPELOEIL_PARAM_LIST9(func_type), \ ::trompeloeil::param_list_t p10 #define TROMPELOEIL_PARAM_LIST9(func_type) \ TROMPELOEIL_PARAM_LIST8(func_type), \ ::trompeloeil::param_list_t p9 #define TROMPELOEIL_PARAM_LIST8(func_type) \ TROMPELOEIL_PARAM_LIST7(func_type), \ ::trompeloeil::param_list_t p8 #define TROMPELOEIL_PARAM_LIST7(func_type) \ TROMPELOEIL_PARAM_LIST6(func_type), \ ::trompeloeil::param_list_t p7 #define TROMPELOEIL_PARAM_LIST6(func_type) \ TROMPELOEIL_PARAM_LIST5(func_type), \ ::trompeloeil::param_list_t p6 #define TROMPELOEIL_PARAM_LIST5(func_type) \ TROMPELOEIL_PARAM_LIST4(func_type), \ ::trompeloeil::param_list_t p5 #define TROMPELOEIL_PARAM_LIST4(func_type) \ TROMPELOEIL_PARAM_LIST3(func_type), \ ::trompeloeil::param_list_t p4 #define TROMPELOEIL_PARAM_LIST3(func_type) \ TROMPELOEIL_PARAM_LIST2(func_type), \ ::trompeloeil::param_list_t p3 #define TROMPELOEIL_PARAM_LIST2(func_type) \ TROMPELOEIL_PARAM_LIST1(func_type), \ ::trompeloeil::param_list_t p2 #define TROMPELOEIL_PARAM_LIST1(func_type) \ ::trompeloeil::param_list_t p1 #define TROMPELOEIL_PARAM_LIST0(func_type) #define TROMPELOEIL_PARAM_LIST(num, func_type) \ TROMPELOEIL_CONCAT(TROMPELOEIL_PARAM_LIST, num)(func_type) #define TROMPELOEIL_PARAMS15 TROMPELOEIL_PARAMS14, p15 #define TROMPELOEIL_PARAMS14 TROMPELOEIL_PARAMS13, p14 #define TROMPELOEIL_PARAMS13 TROMPELOEIL_PARAMS12, p13 #define TROMPELOEIL_PARAMS12 TROMPELOEIL_PARAMS11, p12 #define TROMPELOEIL_PARAMS11 TROMPELOEIL_PARAMS10, p11 #define TROMPELOEIL_PARAMS10 TROMPELOEIL_PARAMS9, p10 #define TROMPELOEIL_PARAMS9 TROMPELOEIL_PARAMS8, p9 #define TROMPELOEIL_PARAMS8 TROMPELOEIL_PARAMS7, p8 #define TROMPELOEIL_PARAMS7 TROMPELOEIL_PARAMS6, p7 #define TROMPELOEIL_PARAMS6 TROMPELOEIL_PARAMS5, p6 #define TROMPELOEIL_PARAMS5 TROMPELOEIL_PARAMS4, p5 #define TROMPELOEIL_PARAMS4 TROMPELOEIL_PARAMS3, p4 #define TROMPELOEIL_PARAMS3 TROMPELOEIL_PARAMS2, p3 #define TROMPELOEIL_PARAMS2 TROMPELOEIL_PARAMS1, p2 #define TROMPELOEIL_PARAMS1 , p1 #define TROMPELOEIL_PARAMS0 #define TROMPELOEIL_PARAMS(num) TROMPELOEIL_CONCAT(TROMPELOEIL_PARAMS, num) #if (TROMPELOEIL_CPLUSPLUS == 201103L) #define TROMPELOEIL_DECLTYPE_AUTO \ auto #define TROMPELOEIL_TRAILING_RETURN_TYPE(return_type) \ -> return_type #else /* (TROMPELOEIL_CPLUSPLUS == 201103L) */ #define TROMPELOEIL_DECLTYPE_AUTO \ decltype(auto) #define TROMPELOEIL_TRAILING_RETURN_TYPE(return_type) \ /**/ #endif /* !(TROMPELOEIL_CPLUSPLUS == 201103L) */ namespace trompeloeil { template struct identity_type { using type = T; }; template identity_type nonconst_member_signature(R (C::*)(Args...)) { return {}; } template identity_type const_member_signature(R (C::*)(Args...) const) { return {}; } # if (TROMPELOEIL_CPLUSPLUS == 201103L) namespace detail { /* Implement C++14 features using only C++11 entities. */ /* */ /* Implementation of make_unique is from * * Stephan T. Lavavej, "make_unique (Revision 1)," * ISO/IEC JTC1 SC22 WG21 N3656, 18 April 2013. * Available: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3656.htm * Accessed: 14 June 2017 */ template struct _Unique_if { typedef std::unique_ptr _Single_object; }; template struct _Unique_if { typedef std::unique_ptr _Unknown_bound; }; template struct _Unique_if { typedef void _Known_bound; }; template typename _Unique_if::_Single_object make_unique(Args&&... args) { return std::unique_ptr(new T(std::forward(args)...)); } template typename _Unique_if::_Unknown_bound make_unique(size_t n) { typedef typename std::remove_extent::type U; return std::unique_ptr(new U[n]()); } template typename _Unique_if::_Known_bound make_unique(Args&&...) = delete; /* */ /* The implementation of these is from * * Walter E. Brown, "TransformationTraits Redux, v2," * ISO/IEC JTC1 SC22 WG21 N3655, 18 April 2013. * Available: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3655.pdf * Accessed: 2 November 2017 * * Minor changes to capitalize template parameter `bool B` has been made. * * See also: * http://en.cppreference.com/w/cpp/types/conditional * http://en.cppreference.com/w/cpp/types/decay * http://en.cppreference.com/w/cpp/types/enable_if * http://en.cppreference.com/w/cpp/types/remove_pointer * http://en.cppreference.com/w/cpp/types/remove_reference * Accessed: 17 May 2017 */ template using conditional_t = typename std::conditional::type; template using decay_t = typename std::decay::type; template using enable_if_t = typename std::enable_if::type; template using remove_pointer_t = typename std::remove_pointer::type; template using remove_reference_t = typename std::remove_reference::type; /* */ /* This implementation of exchange is from * * Jeffrey Yasskin, "exchange() utility function, revision 3," * ISO/IEC JTC1 SC22 WG21 N3688, 19 April 2013. * Available: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3668.html * Accessed: 2 November 2017 * * See also: * http://en.cppreference.com/w/cpp/utility/exchange * Accessed: 17 May 2017 */ template inline T exchange( T& obj, U&& new_value) { T old_value = std::move(obj); obj = std::forward(new_value); return old_value; } /* integer_sequence and index_sequence implemenations are from * * Jonathan Wakely, "Compile-time integer sequences," * ISO/IEC JTC1 SC22 WG21 N3658, 18 April 2013. * Available: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3658.html * Accessed: 2 November 2017 * * See also: * http://en.cppreference.com/w/cpp/utility/integer_sequence * Accessed: 17 May 2017 */ template struct integer_sequence { // Replaces a typedef used in the definition found in N3658. using value_type = T; static constexpr size_t size() noexcept { return sizeof...(I); } }; template using index_sequence = integer_sequence; /* This implementation of make_integer_sequence is from boost/mp11, * * Copyright 2015, 2017 Peter Dimov * * Distributed under the Boost Software License, Version 1.0. * * Implemented here: * * https://github.com/pdimov/mp11/blob/master/include/boost/ * integer_sequence.hpp * Accessed: 17 May 2017 * * (now missing) and here: * * https://github.com/boostorg/mp11/blob/develop/include/boost/ * mp11/integer_sequence.hpp * Accessed: 13 August 2017 */ namespace impl { // iseq_if_c template struct iseq_if_c_impl; template struct iseq_if_c_impl { using type = T; }; template struct iseq_if_c_impl { using type = E; }; template using iseq_if_c = typename iseq_if_c_impl::type; // iseq_identity template struct iseq_identity { using type = T; }; template struct append_integer_sequence; template struct append_integer_sequence, integer_sequence> { using type = integer_sequence; }; template struct make_integer_sequence_impl; template struct make_integer_sequence_impl_ { private: static_assert( N >= 0, "make_integer_sequence: N must not be negative" ); static T const M = N / 2; static T const R = N % 2; using S1 = typename make_integer_sequence_impl::type; using S2 = typename append_integer_sequence::type; using S3 = typename make_integer_sequence_impl::type; using S4 = typename append_integer_sequence::type; public: using type = S4; }; template struct make_integer_sequence_impl: iseq_if_c>, iseq_if_c>, make_integer_sequence_impl_>> { }; } template using make_integer_sequence = typename impl::make_integer_sequence_impl::type; template using make_index_sequence = make_integer_sequence; template using index_sequence_for = make_index_sequence; } /* namespace detail */ # else /* (TROMPELOEIL_CPLUSPLUS == 201103L) */ /* Only these entities really need to * be available in namespace detail, but * VS 2015 has a problem with this approach. * * namespace detail { * * using std::make_unique; * * using std::conditional_t; * using std::decay_t; * using std::enable_if_t; * using std::remove_pointer_t; * using std::remove_reference_t; * * using std::exchange; * using std::index_sequence; * using std::index_sequence_for; * using std::integer_sequence; * using std::make_index_sequence; * * } * * Instead, use a namespace alias. */ namespace detail = std; # endif /* !(TROMPELOEIL_CPLUSPLUS == 201103L) */ class specialized; namespace { inline const std::shared_ptr& get_mutex_obj() { static auto obj = std::make_shared(); return obj; } auto mutex_holder = get_mutex_obj(); } template std::unique_lock get_lock() { return std::unique_lock{ *get_mutex_obj() }; } template using conditional_tuple_element = detail::conditional_t<(N < std::tuple_size::value), typename std::tuple_element::type, int>; template struct param_list; template struct param_list { static size_t constexpr const size = sizeof...(P); template using type = conditional_tuple_element>; }; template using param_list_t = typename param_list::template type; class expectation_violation : public std::logic_error { public: using std::logic_error::logic_error; }; struct location { location() noexcept : file("") , line(0U) {} location( char const* file_, unsigned long line_ ) noexcept : file{file_} , line{line_} {} char const *file; unsigned long line; }; inline std::ostream& operator<<( std::ostream& os, const location& loc) { if (loc.line != 0U) os << loc.file << ':' << loc.line; return os; } enum class severity { fatal, nonfatal }; using reporter_func = std::function; inline void default_reporter( severity, char const *file, unsigned long line, std::string const &msg) { if (!std::current_exception()) { std::stringstream os; os << location{ file, line } << "\n" << msg; throw expectation_violation(os.str()); } } inline reporter_func& reporter_obj() { static reporter_func obj = default_reporter; return obj; } inline reporter_func set_reporter( reporter_func f) { return detail::exchange(reporter_obj(), std::move(f)); } class tracer; inline tracer*& tracer_obj() noexcept { static tracer* ptr = nullptr; return ptr; } inline tracer* set_tracer( tracer* obj) noexcept { // std::exchange would be sane here, but it costs compilation time auto& ptr = tracer_obj(); auto rv = ptr; ptr = obj; return rv; } class tracer { public: virtual void trace( char const *file, unsigned long line, std::string const &call) = 0; protected: tracer() noexcept : previous{set_tracer(this)} {} tracer(tracer const&) = delete; tracer& operator=(tracer const&) = delete; virtual ~tracer() { set_tracer(previous); } private: tracer* previous = nullptr; }; class stream_tracer : public tracer { public: stream_tracer( std::ostream& stream_) : stream(stream_) {} void trace( char const *file, unsigned long line, std::string const &call) override { stream << location{file, line} << '\n' << call << '\n'; } private: std::ostream& stream; }; class trace_agent; template struct reporter; template void send_report( severity s, location loc, std::string const &msg) { reporter::send(s, loc.file, loc.line, msg.c_str()); } template struct reporter { static void send( severity s, char const *file, unsigned long line, char const *msg); }; template void reporter:: send( severity s, char const *file, unsigned long line, char const *msg) { reporter_obj()(s, file, line, msg); } template inline constexpr bool ignore( T const& ...) noexcept { return true; } struct illegal_argument { template constexpr illegal_argument const& operator&() const { static_assert(b, "illegal argument"); return *this; } template constexpr illegal_argument const& operator*() const { static_assert(b, "illegal argument"); return *this; } template constexpr illegal_argument const& operator=(T const&) const { static_assert(b, "illegal argument"); return *this; } template constexpr operator T() const { static_assert(b, "illegal argument"); return {}; } }; template struct void_t_ { using type = void; }; template using void_t = typename void_t_::type; template