diff --git a/include/turtle/detail/mock_impl.hpp b/include/turtle/detail/mock_impl.hpp index 1d57c54..dda080f 100644 --- a/include/turtle/detail/mock_impl.hpp +++ b/include/turtle/detail/mock_impl.hpp @@ -75,7 +75,7 @@ namespace mock { namespace detail { BOOST_PP_TUPLE_ELEM(3, M_n_S_t), \ qualifier) -#define MOCK_METHOD_EXT(name, arity, signature, identifier, qualifiers) \ +#define MOCK_METHOD_EXT_I(name, arity, signature, identifier, qualifiers) \ static_assert(arity == mock::detail::function_arity_t::value, "Arity mismatch"); \ MOCK_PP_TUPLE_FOR_EACH(MOCK_METHOD_ITER, (name, arity, signature, identifier), qualifiers) \ MOCK_METHOD_HELPER(signature, identifier) diff --git a/include/turtle/mock.hpp b/include/turtle/mock.hpp index 0fc7fbc..ecb0041 100644 --- a/include/turtle/mock.hpp +++ b/include/turtle/mock.hpp @@ -72,36 +72,45 @@ } \ MOCK_METHOD_HELPER(void(), identifier) -/// MOCK_METHOD( [calling convention] name, arity[, signature[, identifier]] ) -/// generates both const and non-const methods +/// MOCK_METHOD( [calling convention] name, arity[, signature[, identifier, [, specifiers]]] ) +/// generates both const and non-const methods by default, but can be changed by passing a specifier tuple. /// The 'signature' can be omitted if it can be uniquely identified from the base class /// if 'identifier' is omitted it will default to 'name' -#define MOCK_METHOD(M, ...) \ - MOCK_METHOD_EXT(M, \ - BOOST_PP_VARIADIC_ELEM(0, __VA_ARGS__, ), \ - BOOST_PP_VARIADIC_ELEM(1, __VA_ARGS__, MOCK_SIGNATURE(M), ), \ - BOOST_PP_VARIADIC_ELEM(2, __VA_ARGS__, M, M, ), \ - (const, )) +/// 'specifiers' is a potentially empty, tuple of method specifiers, e.g. (&, &&) or (const override) +#define MOCK_METHOD(M, ...) \ + MOCK_METHOD_EXT_I(M, \ + BOOST_PP_VARIADIC_ELEM(0, __VA_ARGS__, ), \ + BOOST_PP_VARIADIC_ELEM(1, __VA_ARGS__, MOCK_SIGNATURE(M), ), \ + BOOST_PP_VARIADIC_ELEM(2, __VA_ARGS__, M, M, ), \ + BOOST_PP_VARIADIC_ELEM(3, __VA_ARGS__, (const, ), (const, ), (const, ), )) /// MOCK_CONST_METHOD( [calling convention] name, arity[, signature[, identifier]] ) /// generates only the const version of the method /// The 'signature' can be omitted if it can be uniquely identified from the base class /// if 'identifier' is omitted it will default to 'name' -#define MOCK_CONST_METHOD(M, ...) \ - MOCK_METHOD_EXT(M, \ - BOOST_PP_VARIADIC_ELEM(0, __VA_ARGS__, ), \ - BOOST_PP_VARIADIC_ELEM(1, __VA_ARGS__, MOCK_SIGNATURE(M), ), \ - BOOST_PP_VARIADIC_ELEM(2, __VA_ARGS__, M, M, ), \ - (const)) +#define MOCK_CONST_METHOD(M, ...) \ + MOCK_METHOD_EXT_I(M, \ + BOOST_PP_VARIADIC_ELEM(0, __VA_ARGS__, ), \ + BOOST_PP_VARIADIC_ELEM(1, __VA_ARGS__, MOCK_SIGNATURE(M), ), \ + BOOST_PP_VARIADIC_ELEM(2, __VA_ARGS__, M, M, ), \ + (const)) /// MOCK_NON_CONST_METHOD( [calling convention] name, arity[, signature[, identifier]] ) /// generates only the non-const version of the method /// The 'signature' can be omitted if it can be uniquely identified from the base class /// if 'identifier' is omitted it will default to 'name' -#define MOCK_NON_CONST_METHOD(M, ...) \ - MOCK_METHOD_EXT(M, \ - BOOST_PP_VARIADIC_ELEM(0, __VA_ARGS__, ), \ - BOOST_PP_VARIADIC_ELEM(1, __VA_ARGS__, MOCK_SIGNATURE(M), ), \ - BOOST_PP_VARIADIC_ELEM(2, __VA_ARGS__, M, M, ), \ - ()) +#define MOCK_NON_CONST_METHOD(M, ...) \ + MOCK_METHOD_EXT_I(M, \ + BOOST_PP_VARIADIC_ELEM(0, __VA_ARGS__, ), \ + BOOST_PP_VARIADIC_ELEM(1, __VA_ARGS__, MOCK_SIGNATURE(M), ), \ + BOOST_PP_VARIADIC_ELEM(2, __VA_ARGS__, M, M, ), \ + ()) + +/// MOCK_METHOD_EXT( [calling convention] name, arity, qualifiers [, signature, [identifier]] ) +#define MOCK_METHOD_EXT(M, arity, ...) \ + MOCK_METHOD_EXT_I(M, \ + arity, \ + BOOST_PP_VARIADIC_ELEM(1, __VA_ARGS__, MOCK_SIGNATURE(M), ), \ + BOOST_PP_VARIADIC_ELEM(2, __VA_ARGS__, M, M, ), \ + BOOST_PP_VARIADIC_ELEM(0, __VA_ARGS__, )) /// MOCK_FUNCTION( [calling convention] name, arity, signature[, identifier] ) /// if 'identifier' is omitted it will default to 'name' diff --git a/test/test_mock.cpp b/test/test_mock.cpp index a61c305..eb4b562 100644 --- a/test/test_mock.cpp +++ b/test/test_mock.cpp @@ -327,6 +327,15 @@ struct base virtual void m1() = 0; virtual void m10() const = 0; virtual void m11() = 0; + virtual void m12() noexcept = 0; + virtual void m13() && = 0; + virtual void m14() = 0; + virtual void m14(int, float) = 0; + virtual void m14(int, int) = 0; + virtual void m14(int) = 0; + virtual void m14(int) const noexcept = 0; + virtual void m15() & = 0; + virtual void m15() && = 0; }; MOCK_BASE_CLASS(variadic, base) @@ -340,9 +349,28 @@ MOCK_BASE_CLASS(variadic, base) MOCK_NON_CONST_METHOD(m11, 0) MOCK_NON_CONST_METHOD(m6, 0, void()) MOCK_NON_CONST_METHOD(m7, 0, void(), m7) + + MOCK_METHOD_EXT(m12, 0, (noexcept override)) + MOCK_METHOD_EXT(m13, 0, (&&override)) + // Overloaded method + MOCK_METHOD(m14, 0, void(), m14_empty) + MOCK_METHOD(m14, 2, void(int, float), m14_int_float, ()) + MOCK_METHOD(m14, 2, void(int, int), m14_int_int, (override)) + MOCK_METHOD(m14, 1, void(int), m14_int, (override, const noexcept override)) + MOCK_METHOD_EXT(m15, 0, (&override, &&override), void()) MOCK_STATIC_METHOD(m8, 0, void()) MOCK_STATIC_METHOD(m9, 0, void(), m9) }; +void instantiate_class() +{ + variadic inst; // If this compiles all pure virtual methods were mocked + const variadic& cinst = inst; + static_assert(noexcept(inst.m12()), "noexcept should be kept"); + static_assert(!noexcept(inst.m14()), "noexcept should not be set"); + static_assert(noexcept(cinst.m14(1)), "noexcept should be kept"); + static_assert(!noexcept(inst.m14(1)), "noexcept should not be set"); + static_assert(noexcept(std::declval().m14(1)), "noexcept should be kept"); +} template MOCK_BASE_CLASS(variadic_tpl, base)