/* * Trompeloeil C++ mocking framework * * Copyright Björn Fahller 2014-2017 * * 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 # 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 <= 201103) # error requires C++14 or higher # endif #endif #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) namespace trompeloeil { 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 = std::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 std::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