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 */ 16package org.easymock.internal; 17 18import java.io.Serializable; 19import java.lang.reflect.Method; 20import java.util.ArrayList; 21import java.util.List; 22 23import org.easymock.EasyMock; 24 25public 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 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 56 public final void addStub(ExpectedInvocation expected, Result result) { 57 stubResults.add(new ExpectedInvocationAndResult(expected, result)); 58 } 59 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 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 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 87 private UnorderedBehavior lastBehaviorList() { 88 return behaviorLists.get(behaviorLists.size() - 1); 89 } 90 91 @SuppressWarnings("deprecation") 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 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 193 public void checkOrder(boolean value) { 194 this.checkOrder = value; 195 } 196 197 public void makeThreadSafe(boolean isThreadSafe) { 198 this.isThreadSafe = isThreadSafe; 199 } 200 201 public void shouldBeUsedInOneThread(boolean shouldBeUsedInOneThread) { 202 this.shouldBeUsedInOneThread = shouldBeUsedInOneThread; 203 } 204 205 public boolean isThreadSafe() { 206 return this.isThreadSafe; 207 } 208 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 223 public LegacyMatcherProvider getLegacyMatcherProvider() { 224 if (legacyMatcherProvider == null) { 225 legacyMatcherProvider = new LegacyMatcherProvider(); 226 } 227 return legacyMatcherProvider; 228 } 229 230 @SuppressWarnings("deprecation") 231 public void setDefaultMatcher(org.easymock.ArgumentsMatcher matcher) { 232 getLegacyMatcherProvider().setDefaultMatcher(matcher); 233 } 234 235 @SuppressWarnings("deprecation") 236 public void setMatcher(Method method, org.easymock.ArgumentsMatcher matcher) { 237 getLegacyMatcherProvider().setMatcher(method, matcher); 238 } 239} 240