1 /*
2  * Copyright 2001-2009 OFFIS, Tammo Freese
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package org.easymock.internal;
17 
18 import java.io.Serializable;
19 import java.lang.reflect.Method;
20 import java.util.ArrayList;
21 import java.util.List;
22 
23 import org.easymock.EasyMock;
24 
25 public class MocksBehavior implements IMocksBehavior, Serializable {
26 
27     private static final long serialVersionUID = 3265727009370529027L;
28 
29     private final List<UnorderedBehavior> behaviorLists = new ArrayList<UnorderedBehavior>();
30 
31     private final List<ExpectedInvocationAndResult> stubResults = new ArrayList<ExpectedInvocationAndResult>();
32 
33     private final boolean nice;
34 
35     private boolean checkOrder;
36 
37     private boolean isThreadSafe;
38 
39     private boolean shouldBeUsedInOneThread;
40 
41     private int position = 0;
42 
43     private transient volatile Thread lastThread;
44 
45     private LegacyMatcherProvider legacyMatcherProvider;
46 
MocksBehavior(boolean nice)47     public MocksBehavior(boolean nice) {
48         this.nice = nice;
49         this.isThreadSafe = !Boolean.valueOf(EasyMockProperties.getInstance()
50                 .getProperty(EasyMock.NOT_THREAD_SAFE_BY_DEFAULT));
51         this.shouldBeUsedInOneThread = Boolean.valueOf(EasyMockProperties
52                 .getInstance().getProperty(
53                         EasyMock.ENABLE_THREAD_SAFETY_CHECK_BY_DEFAULT));
54     }
55 
addStub(ExpectedInvocation expected, Result result)56     public final void addStub(ExpectedInvocation expected, Result result) {
57         stubResults.add(new ExpectedInvocationAndResult(expected, result));
58     }
59 
addExpected(ExpectedInvocation expected, Result result, Range count)60     public void addExpected(ExpectedInvocation expected, Result result,
61             Range count) {
62         if (legacyMatcherProvider != null) {
63             expected = expected.withMatcher(legacyMatcherProvider
64                     .getMatcher(expected.getMethod()));
65         }
66         addBehaviorListIfNecessary(expected);
67         lastBehaviorList().addExpected(expected, result, count);
68     }
69 
getStubResult(Invocation actual)70     private final Result getStubResult(Invocation actual) {
71         for (ExpectedInvocationAndResult each : stubResults) {
72             if (each.getExpectedInvocation().matches(actual)) {
73                 return each.getResult();
74             }
75         }
76         return null;
77     }
78 
addBehaviorListIfNecessary(ExpectedInvocation expected)79     private void addBehaviorListIfNecessary(ExpectedInvocation expected) {
80         if (behaviorLists.isEmpty()
81                 || !lastBehaviorList().allowsExpectedInvocation(expected,
82                         checkOrder)) {
83             behaviorLists.add(new UnorderedBehavior(checkOrder));
84         }
85     }
86 
lastBehaviorList()87     private UnorderedBehavior lastBehaviorList() {
88         return behaviorLists.get(behaviorLists.size() - 1);
89     }
90 
91     @SuppressWarnings("deprecation")
addActual(Invocation actual)92     public final Result addActual(Invocation actual) {
93         int initialPosition = position;
94 
95         while (position < behaviorLists.size()) {
96             Result result = behaviorLists.get(position).addActual(actual);
97             if (result != null) {
98                 return result;
99             }
100             if (!behaviorLists.get(position).verify()) {
101                 break;
102             }
103             position++;
104         }
105         Result stubOrNice = getStubResult(actual);
106         if (stubOrNice == null && nice) {
107             stubOrNice = Result.createReturnResult(RecordState
108                     .emptyReturnValueFor(actual.getMethod().getReturnType()));
109         }
110 
111         int endPosition = position;
112 
113         // Do not move the cursor in case of stub, nice or error
114         position = initialPosition;
115 
116         if (stubOrNice != null) {
117             actual.validateCaptures();
118             actual.clearCaptures();
119             return stubOrNice;
120         }
121 
122         // Case where the loop was exited at the end of the behaviorLists
123         if (endPosition == behaviorLists.size()) {
124             endPosition--;
125         }
126 
127         // Loop all around the behaviors left to generate the message
128         StringBuilder errorMessage = new StringBuilder(70 * (endPosition
129                 - initialPosition + 1)); // rough approximation of the length
130         errorMessage.append("\n  Unexpected method call ").append(
131                 actual.toString(org.easymock.MockControl.EQUALS_MATCHER));
132 
133         List<ErrorMessage> messages = new ArrayList<ErrorMessage>();
134 
135         int matches = 0;
136 
137         // First find how many match we have
138         for (int i = initialPosition; i <= endPosition; i++) {
139             List<ErrorMessage> thisListMessages = behaviorLists.get(i)
140                     .getMessages(actual);
141             messages.addAll(thisListMessages);
142             for (ErrorMessage m : thisListMessages) {
143                 if (m.isMatching()) {
144                     matches++;
145                 }
146             }
147         }
148 
149         if (matches > 1) {
150             errorMessage
151                     .append(". Possible matches are marked with (+1):");
152         } else {
153             errorMessage.append(":");
154         }
155 
156         for (ErrorMessage m : messages) {
157             m.appendTo(errorMessage, matches);
158         }
159 
160         // And finally throw the error
161         throw new AssertionErrorWrapper(new AssertionError(errorMessage));
162     }
163 
verify()164     public void verify() {
165         boolean verified = true;
166 
167         for (UnorderedBehavior behaviorList : behaviorLists.subList(position,
168                 behaviorLists.size())) {
169             if (!behaviorList.verify()) {
170                 verified = false;
171             }
172         }
173         if (verified) {
174             return;
175         }
176 
177         StringBuilder errorMessage = new StringBuilder(70 * (behaviorLists
178                 .size()
179                 - position + 1));
180 
181         errorMessage.append("\n  Expectation failure on verify:");
182         for (UnorderedBehavior behaviorList : behaviorLists.subList(position,
183                 behaviorLists.size())) {
184             for (ErrorMessage m : behaviorList.getMessages(null)) {
185                 m.appendTo(errorMessage, 0);
186             }
187         }
188 
189         throw new AssertionErrorWrapper(new AssertionError(errorMessage
190                 .toString()));
191     }
192 
checkOrder(boolean value)193     public void checkOrder(boolean value) {
194         this.checkOrder = value;
195     }
196 
makeThreadSafe(boolean isThreadSafe)197     public void makeThreadSafe(boolean isThreadSafe) {
198         this.isThreadSafe = isThreadSafe;
199     }
200 
shouldBeUsedInOneThread(boolean shouldBeUsedInOneThread)201     public void shouldBeUsedInOneThread(boolean shouldBeUsedInOneThread) {
202         this.shouldBeUsedInOneThread = shouldBeUsedInOneThread;
203     }
204 
isThreadSafe()205     public boolean isThreadSafe() {
206         return this.isThreadSafe;
207     }
208 
checkThreadSafety()209     public void checkThreadSafety() {
210         if (!shouldBeUsedInOneThread) {
211             return;
212         }
213         if (lastThread == null) {
214             lastThread = Thread.currentThread();
215         } else if(lastThread != Thread.currentThread()) {
216             throw new AssertionErrorWrapper(new AssertionError(
217                     "\n Mock isn't supposed to be called from multiple threads. Last: "
218                             + lastThread +
219                     " Current: " + Thread.currentThread()));
220         }
221     }
222 
getLegacyMatcherProvider()223     public LegacyMatcherProvider getLegacyMatcherProvider() {
224         if (legacyMatcherProvider == null) {
225             legacyMatcherProvider = new LegacyMatcherProvider();
226         }
227         return legacyMatcherProvider;
228     }
229 
230     @SuppressWarnings("deprecation")
setDefaultMatcher(org.easymock.ArgumentsMatcher matcher)231     public void setDefaultMatcher(org.easymock.ArgumentsMatcher matcher) {
232         getLegacyMatcherProvider().setDefaultMatcher(matcher);
233     }
234 
235     @SuppressWarnings("deprecation")
setMatcher(Method method, org.easymock.ArgumentsMatcher matcher)236     public void setMatcher(Method method, org.easymock.ArgumentsMatcher matcher) {
237         getLegacyMatcherProvider().setMatcher(method, matcher);
238     }
239 }
240