1 /*
2  * Copyright (c) 2007 Mockito contributors
3  * This program is made available under the terms of the MIT License.
4  */
5 package org.mockito.internal.stubbing;
6 
7 import org.mockito.internal.invocation.StubInfoImpl;
8 import org.mockito.internal.verification.DefaultRegisteredInvocations;
9 import org.mockito.internal.verification.RegisteredInvocations;
10 import org.mockito.internal.verification.SingleRegisteredInvocation;
11 import org.mockito.invocation.Invocation;
12 import org.mockito.invocation.InvocationContainer;
13 import org.mockito.invocation.MatchableInvocation;
14 import org.mockito.mock.MockCreationSettings;
15 import org.mockito.quality.Strictness;
16 import org.mockito.stubbing.Answer;
17 import org.mockito.stubbing.Stubbing;
18 import org.mockito.stubbing.ValidableAnswer;
19 
20 import java.io.Serializable;
21 import java.util.Collection;
22 import java.util.Collections;
23 import java.util.LinkedList;
24 import java.util.List;
25 
26 import static org.mockito.internal.progress.ThreadSafeMockingProgress.mockingProgress;
27 
28 @SuppressWarnings("unchecked")
29 public class InvocationContainerImpl implements InvocationContainer, Serializable {
30 
31     private static final long serialVersionUID = -5334301962749537177L;
32     private final LinkedList<StubbedInvocationMatcher> stubbed = new LinkedList<StubbedInvocationMatcher>();
33     private final DoAnswerStyleStubbing doAnswerStyleStubbing;
34     private final RegisteredInvocations registeredInvocations;
35     private final Strictness mockStrictness;
36 
37     private MatchableInvocation invocationForStubbing;
38 
InvocationContainerImpl(MockCreationSettings mockSettings)39     public InvocationContainerImpl(MockCreationSettings mockSettings) {
40         this.registeredInvocations = createRegisteredInvocations(mockSettings);
41         this.mockStrictness = mockSettings.isLenient() ? Strictness.LENIENT : null;
42         this.doAnswerStyleStubbing = new DoAnswerStyleStubbing();
43     }
44 
setInvocationForPotentialStubbing(MatchableInvocation invocation)45     public void setInvocationForPotentialStubbing(MatchableInvocation invocation) {
46         registeredInvocations.add(invocation.getInvocation());
47         this.invocationForStubbing = invocation;
48     }
49 
resetInvocationForPotentialStubbing(MatchableInvocation invocationMatcher)50     public void resetInvocationForPotentialStubbing(MatchableInvocation invocationMatcher) {
51         this.invocationForStubbing = invocationMatcher;
52     }
53 
addAnswer(Answer answer, Strictness stubbingStrictness)54     public void addAnswer(Answer answer, Strictness stubbingStrictness) {
55         registeredInvocations.removeLast();
56         addAnswer(answer, false, stubbingStrictness);
57     }
58 
addConsecutiveAnswer(Answer answer)59     public void addConsecutiveAnswer(Answer answer) {
60         addAnswer(answer, true, null);
61     }
62 
63     /**
64      * Adds new stubbed answer and returns the invocation matcher the answer was added to.
65      */
addAnswer(Answer answer, boolean isConsecutive, Strictness stubbingStrictness)66     public StubbedInvocationMatcher addAnswer(Answer answer, boolean isConsecutive, Strictness stubbingStrictness) {
67         Invocation invocation = invocationForStubbing.getInvocation();
68         mockingProgress().stubbingCompleted();
69         if (answer instanceof ValidableAnswer) {
70             ((ValidableAnswer) answer).validateFor(invocation);
71         }
72 
73         synchronized (stubbed) {
74             if (isConsecutive) {
75                 stubbed.getFirst().addAnswer(answer);
76             } else {
77                 Strictness effectiveStrictness = stubbingStrictness != null ? stubbingStrictness : this.mockStrictness;
78                 stubbed.addFirst(new StubbedInvocationMatcher(answer, invocationForStubbing, effectiveStrictness));
79             }
80             return stubbed.getFirst();
81         }
82     }
83 
answerTo(Invocation invocation)84     Object answerTo(Invocation invocation) throws Throwable {
85         return findAnswerFor(invocation).answer(invocation);
86     }
87 
findAnswerFor(Invocation invocation)88     public StubbedInvocationMatcher findAnswerFor(Invocation invocation) {
89         synchronized (stubbed) {
90             for (StubbedInvocationMatcher s : stubbed) {
91                 if (s.matches(invocation)) {
92                     s.markStubUsed(invocation);
93                     //TODO we should mark stubbed at the point of stubbing, not at the point where the stub is being used
94                     invocation.markStubbed(new StubInfoImpl(s));
95                     return s;
96                 }
97             }
98         }
99 
100         return null;
101     }
102 
103     /**
104      * Sets the answers declared with 'doAnswer' style.
105      */
setAnswersForStubbing(List<Answer<?>> answers, Strictness strictness)106     public void setAnswersForStubbing(List<Answer<?>> answers, Strictness strictness) {
107         doAnswerStyleStubbing.setAnswers(answers, strictness);
108     }
109 
hasAnswersForStubbing()110     public boolean hasAnswersForStubbing() {
111         return !doAnswerStyleStubbing.isSet();
112     }
113 
hasInvocationForPotentialStubbing()114     public boolean hasInvocationForPotentialStubbing() {
115         return !registeredInvocations.isEmpty();
116     }
117 
setMethodForStubbing(MatchableInvocation invocation)118     public void setMethodForStubbing(MatchableInvocation invocation) {
119         invocationForStubbing = invocation;
120         assert hasAnswersForStubbing();
121         for (int i = 0; i < doAnswerStyleStubbing.getAnswers().size(); i++) {
122             addAnswer(doAnswerStyleStubbing.getAnswers().get(i), i != 0, doAnswerStyleStubbing.getStubbingStrictness());
123         }
124         doAnswerStyleStubbing.clear();
125     }
126 
127     @Override
toString()128     public String toString() {
129         return "invocationForStubbing: " + invocationForStubbing;
130     }
131 
getInvocations()132     public List<Invocation> getInvocations() {
133         return registeredInvocations.getAll();
134     }
135 
clearInvocations()136     public void clearInvocations() {
137         registeredInvocations.clear();
138     }
139 
140     /**
141      * Stubbings in descending order, most recent first
142      */
getStubbingsDescending()143     public List<Stubbing> getStubbingsDescending() {
144         return (List) stubbed;
145     }
146 
147     /**
148      * Stubbings in ascending order, most recent last
149      */
getStubbingsAscending()150     public Collection<Stubbing> getStubbingsAscending() {
151         List<Stubbing> result = new LinkedList<Stubbing>(stubbed);
152         Collections.reverse(result);
153         return result;
154     }
155 
invokedMock()156     public Object invokedMock() {
157         return invocationForStubbing.getInvocation().getMock();
158     }
159 
getInvocationForStubbing()160     public MatchableInvocation getInvocationForStubbing() {
161         return invocationForStubbing;
162     }
163 
createRegisteredInvocations(MockCreationSettings mockSettings)164     private RegisteredInvocations createRegisteredInvocations(MockCreationSettings mockSettings) {
165         return mockSettings.isStubOnly()
166           ? new SingleRegisteredInvocation()
167           : new DefaultRegisteredInvocations();
168     }
169 }
170