/* * Copyright (c) 2014 Eran Pe'er. * * This program is made available under the terms of the MIT License. * * Created on Mar 10, 2014 */ #pragma once #include #include #include #include #include #include #include #include #include #include "fakeit/RecordedMethodBody.hpp" #include "fakeit/StubbingProgress.hpp" #include "fakeit/Sequence.hpp" #include "fakeit/ActualInvocation.hpp" #include "fakeit/EventHandler.hpp" #include "fakeit/ActionSequence.hpp" #include "fakeit/DomainObjects.hpp" #include "fakeit/SpyingContext.hpp" #include "fakeit/StubbingContext.hpp" #include "fakeit/MatchersCollector.hpp" #include "mockutils/type_utils.hpp" namespace fakeit { /** * Build recorded sequence and the matching criteria. * For example, for the following line: * When(Method(mock,foo)).Return(1).Return(2_Times(2)).Throw(e1); * The matching criteria is: Any invocation of mock.foo * The recorded sequence is: {Return(1), Return(2), Return(2), Throw(e1)} */ template class MethodMockingContext : public Sequence, // For use in Verify(sequence1,...)... phrases. public ActualInvocationsSource, // For use in Using(source1,souece2,...) and VerifyNoOtherInvocations(source1,souece2...) phrases. public virtual StubbingContext, // For use in Fake & When phrases public virtual SpyingContext, // For use in Spy phrases private Invocation::Matcher { public: struct Context : public Destructible { virtual ~Context() = default; /** * Return the original method. not the mock. */ virtual std::function getOriginalMethod() = 0; virtual std::string getMethodName() = 0; virtual void addMethodInvocationHandler(typename ActualInvocation::Matcher *matcher, MethodInvocationHandler *invocationHandler) = 0; virtual void scanActualInvocations(const std::function &)> &scanner) = 0; virtual void setMethodDetails(std::string mockName, std::string methodName) = 0; virtual bool isOfMethod(MethodInfo &method) = 0; virtual ActualInvocationsSource &getInvolvedMock() = 0; }; private: class Implementation { Context *_stubbingContext; ActionSequence *_recordedActionSequence; typename ActualInvocation::Matcher *_invocationMatcher; bool _commited; Context &getStubbingContext() const { return *_stubbingContext; } public: Implementation(Context *stubbingContext) : _stubbingContext(stubbingContext), _recordedActionSequence(new ActionSequence()), _invocationMatcher { new DefaultInvocationMatcher()}, _commited(false) { } ~Implementation() { delete _stubbingContext; if (!_commited) { // no commit. delete the created objects. delete _recordedActionSequence; delete _invocationMatcher; } } ActionSequence &getRecordedActionSequence() { return *_recordedActionSequence; } std::string format() const { std::string s = getStubbingContext().getMethodName(); s += _invocationMatcher->format(); return s; } void getActualInvocations(std::unordered_set &into) const { auto scanner = [&](ActualInvocation &a) { if (_invocationMatcher->matches(a)) { into.insert(&a); } }; getStubbingContext().scanActualInvocations(scanner); } /** * Used only by Verify phrase. */ bool matches(Invocation &invocation) { MethodInfo &actualMethod = invocation.getMethod(); if (!getStubbingContext().isOfMethod(actualMethod)) { return false; } ActualInvocation &actualInvocation = dynamic_cast &>(invocation); return _invocationMatcher->matches(actualInvocation); } void commit() { getStubbingContext().addMethodInvocationHandler(_invocationMatcher, _recordedActionSequence); _commited = true; } void appendAction(Action *action) { getRecordedActionSequence().AppendDo(action); } void setMethodBodyByAssignment(std::function method) { appendAction(new RepeatForever(method)); commit(); } void setMethodDetails(std::string mockName, std::string methodName) { getStubbingContext().setMethodDetails(mockName, methodName); } void getInvolvedMocks(std::vector &into) const { into.push_back(&getStubbingContext().getInvolvedMock()); } typename std::function getOriginalMethod() { return getStubbingContext().getOriginalMethod(); } void setInvocationMatcher(typename ActualInvocation::Matcher *matcher) { delete _invocationMatcher; _invocationMatcher = matcher; } }; protected: MethodMockingContext(Context *stubbingContext) : _impl{new Implementation(stubbingContext)} { } MethodMockingContext(MethodMockingContext &) = default; //we have to write move ctor by hand since VC 2013 doesn't support defaulted //move constructor and move assignment MethodMockingContext(MethodMockingContext &&other) : _impl(std::move(other._impl)) { } virtual ~MethodMockingContext() { } std::string format() const { return _impl->format(); } unsigned int size() const override { return 1; } /** * Used only by Verify phrase. */ void getInvolvedMocks(std::vector &into) const override { _impl->getInvolvedMocks(into); } void getExpectedSequence(std::vector &into) const override { const Invocation::Matcher *b = this; Invocation::Matcher *c = const_cast(b); into.push_back(c); } /** * Used only by Verify phrase. */ void getActualInvocations(std::unordered_set &into) const override { _impl->getActualInvocations(into); } /** * Used only by Verify phrase. */ bool matches(Invocation &invocation) override { return _impl->matches(invocation); } void commit() override { _impl->commit(); } void setMethodDetails(std::string mockName, std::string methodName) { _impl->setMethodDetails(mockName, methodName); } void setMatchingCriteria(std::function predicate) { typename ActualInvocation::Matcher *matcher{ new UserDefinedInvocationMatcher(predicate)}; _impl->setInvocationMatcher(matcher); } void setMatchingCriteria(const std::vector &matchers) { typename ActualInvocation::Matcher *matcher{ new ArgumentsMatcherInvocationMatcher(matchers)}; _impl->setInvocationMatcher(matcher); } /** * Used by Fake, Spy & When functors */ void appendAction(Action *action) override { _impl->appendAction(action); } void setMethodBodyByAssignment(std::function method) { _impl->setMethodBodyByAssignment(method); } template::type> void setMatchingCriteria(const matcherCreators &... matcherCreator) { std::vector matchers; MatchersCollector<0, arglist...> c(matchers); c.CollectMatchers(matcherCreator...); MethodMockingContext::setMatchingCriteria(matchers); } private: std::function getOriginalMethod() override { return _impl->getOriginalMethod(); } std::shared_ptr _impl; }; template class MockingContext : public MethodMockingContext { MockingContext &operator=(const MockingContext &) = delete; public: MockingContext(typename MethodMockingContext::Context *stubbingContext) : MethodMockingContext(stubbingContext) { } MockingContext(MockingContext &) = default; MockingContext(MockingContext &&other) : MethodMockingContext(std::move(other)) { } MockingContext &setMethodDetails(std::string mockName, std::string methodName) { MethodMockingContext::setMethodDetails(mockName, methodName); return *this; } MockingContext &Using(const arglist &... args) { MethodMockingContext::setMatchingCriteria(args...); return *this; } template MockingContext &Using(const arg_matcher &... arg_matchers) { MethodMockingContext::setMatchingCriteria(arg_matchers...); return *this; } MockingContext &Matching(std::function matcher) { MethodMockingContext::setMatchingCriteria(matcher); return *this; } MockingContext &operator()(const arglist &... args) { MethodMockingContext::setMatchingCriteria(args...); return *this; } MockingContext &operator()(std::function matcher) { MethodMockingContext::setMatchingCriteria(matcher); return *this; } void operator=(std::function method) { MethodMockingContext::setMethodBodyByAssignment(method); } template typename std::enable_if::value, void>::type operator=(const R &r) { auto method = [r](arglist &...) -> R { return r; }; MethodMockingContext::setMethodBodyByAssignment(method); } template typename std::enable_if::value, void>::type operator=(const R &r) { auto method = [&r](arglist &...) -> R { return r; }; MethodMockingContext::setMethodBodyByAssignment(method); } }; template class MockingContext : public MethodMockingContext { MockingContext &operator=(const MockingContext &) = delete; public: MockingContext(typename MethodMockingContext::Context *stubbingContext) : MethodMockingContext(stubbingContext) { } MockingContext(MockingContext &) = default; MockingContext(MockingContext &&other) : MethodMockingContext(std::move(other)) { } MockingContext &setMethodDetails(std::string mockName, std::string methodName) { MethodMockingContext::setMethodDetails(mockName, methodName); return *this; } MockingContext &Using(const arglist &... args) { MethodMockingContext::setMatchingCriteria(args...); return *this; } template MockingContext &Using(const arg_matcher &... arg_matchers) { MethodMockingContext::setMatchingCriteria(arg_matchers...); return *this; } MockingContext &Matching(std::function matcher) { MethodMockingContext::setMatchingCriteria(matcher); return *this; } MockingContext &operator()(const arglist &... args) { MethodMockingContext::setMatchingCriteria(args...); return *this; } MockingContext &operator()(std::function matcher) { MethodMockingContext::setMatchingCriteria(matcher); return *this; } void operator=(std::function method) { MethodMockingContext::setMethodBodyByAssignment(method); } }; class DtorMockingContext : public MethodMockingContext { public: DtorMockingContext(MethodMockingContext::Context *stubbingContext) : MethodMockingContext(stubbingContext) { } DtorMockingContext(DtorMockingContext &other) : MethodMockingContext(other) { } DtorMockingContext(DtorMockingContext &&other) : MethodMockingContext(std::move(other)) { } void operator=(std::function method) { MethodMockingContext::setMethodBodyByAssignment(method); } DtorMockingContext &setMethodDetails(std::string mockName, std::string methodName) { MethodMockingContext::setMethodDetails(mockName, methodName); return *this; } }; }