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.HashMap; 21 import java.util.List; 22 import java.util.Map; 23 24 import org.easymock.IAnswer; 25 import org.easymock.IArgumentMatcher; 26 27 public class RecordState implements IMocksControlState, Serializable { 28 29 private static final long serialVersionUID = -5418279681566430252L; 30 31 private ExpectedInvocation lastInvocation = null; 32 33 private boolean lastInvocationUsed = true; 34 35 private Result lastResult; 36 37 private final IMocksBehavior behavior; 38 39 private static Map<Class<?>, Object> emptyReturnValues = new HashMap<Class<?>, Object>(); 40 41 static { emptyReturnValues.put(Void.TYPE, null)42 emptyReturnValues.put(Void.TYPE, null); emptyReturnValues.put(Boolean.TYPE, Boolean.FALSE)43 emptyReturnValues.put(Boolean.TYPE, Boolean.FALSE); emptyReturnValues.put(Byte.TYPE, Byte.valueOf((byte) 0))44 emptyReturnValues.put(Byte.TYPE, Byte.valueOf((byte) 0)); emptyReturnValues.put(Short.TYPE, Short.valueOf((short) 0))45 emptyReturnValues.put(Short.TYPE, Short.valueOf((short) 0)); emptyReturnValues.put(Character.TYPE, Character.valueOf((char)0))46 emptyReturnValues.put(Character.TYPE, Character.valueOf((char)0)); emptyReturnValues.put(Integer.TYPE, Integer.valueOf(0))47 emptyReturnValues.put(Integer.TYPE, Integer.valueOf(0)); emptyReturnValues.put(Long.TYPE, Long.valueOf(0))48 emptyReturnValues.put(Long.TYPE, Long.valueOf(0)); emptyReturnValues.put(Float.TYPE, Float.valueOf(0))49 emptyReturnValues.put(Float.TYPE, Float.valueOf(0)); emptyReturnValues.put(Double.TYPE, Double.valueOf(0))50 emptyReturnValues.put(Double.TYPE, Double.valueOf(0)); 51 } 52 53 private static Map<Class<?>, Class<?>> primitiveToWrapperType = new HashMap<Class<?>, Class<?>>(); 54 55 static { primitiveToWrapperType.put(Boolean.TYPE, Boolean.class)56 primitiveToWrapperType.put(Boolean.TYPE, Boolean.class); primitiveToWrapperType.put(Byte.TYPE, Byte.class)57 primitiveToWrapperType.put(Byte.TYPE, Byte.class); primitiveToWrapperType.put(Short.TYPE, Short.class)58 primitiveToWrapperType.put(Short.TYPE, Short.class); primitiveToWrapperType.put(Character.TYPE, Character.class)59 primitiveToWrapperType.put(Character.TYPE, Character.class); primitiveToWrapperType.put(Integer.TYPE, Integer.class)60 primitiveToWrapperType.put(Integer.TYPE, Integer.class); primitiveToWrapperType.put(Long.TYPE, Long.class)61 primitiveToWrapperType.put(Long.TYPE, Long.class); primitiveToWrapperType.put(Float.TYPE, Float.class)62 primitiveToWrapperType.put(Float.TYPE, Float.class); primitiveToWrapperType.put(Double.TYPE, Double.class)63 primitiveToWrapperType.put(Double.TYPE, Double.class); 64 } 65 RecordState(IMocksBehavior behavior)66 public RecordState(IMocksBehavior behavior) { 67 this.behavior = behavior; 68 } 69 assertRecordState()70 public void assertRecordState() { 71 } 72 invoke(Invocation invocation)73 public java.lang.Object invoke(Invocation invocation) { 74 closeMethod(); 75 List<IArgumentMatcher> lastMatchers = LastControl.pullMatchers(); 76 lastInvocation = new ExpectedInvocation(invocation, lastMatchers); 77 lastInvocationUsed = false; 78 return emptyReturnValueFor(invocation.getMethod().getReturnType()); 79 } 80 replay()81 public void replay() { 82 closeMethod(); 83 if (LastControl.pullMatchers() != null) { 84 throw new IllegalStateException("matcher calls were used outside expectations"); 85 } 86 } 87 verify()88 public void verify() { 89 throw new RuntimeExceptionWrapper(new IllegalStateException( 90 "calling verify is not allowed in record state")); 91 } 92 andReturn(Object value)93 public void andReturn(Object value) { 94 requireMethodCall("return value"); 95 value = convertNumberClassIfNeccessary(value); 96 requireAssignable(value); 97 if (lastResult != null) { 98 times(MocksControl.ONCE); 99 } 100 lastResult = Result.createReturnResult(value); 101 } 102 andThrow(Throwable throwable)103 public void andThrow(Throwable throwable) { 104 requireMethodCall("Throwable"); 105 requireValidThrowable(throwable); 106 if (lastResult != null) { 107 times(MocksControl.ONCE); 108 } 109 lastResult = Result.createThrowResult(throwable); 110 } 111 andAnswer(IAnswer<?> answer)112 public void andAnswer(IAnswer<?> answer) { 113 requireMethodCall("answer"); 114 requireValidAnswer(answer); 115 if (lastResult != null) { 116 times(MocksControl.ONCE); 117 } 118 lastResult = Result.createAnswerResult(answer); 119 } 120 andDelegateTo(Object delegateTo)121 public void andDelegateTo(Object delegateTo) { 122 requireMethodCall("delegate"); 123 requireValidDelegation(delegateTo); 124 if (lastResult != null) { 125 times(MocksControl.ONCE); 126 } 127 lastResult = Result.createDelegatingResult(delegateTo); 128 } 129 andStubReturn(Object value)130 public void andStubReturn(Object value) { 131 requireMethodCall("stub return value"); 132 value = convertNumberClassIfNeccessary(value); 133 requireAssignable(value); 134 if (lastResult != null) { 135 times(MocksControl.ONCE); 136 } 137 behavior.addStub(lastInvocation, Result.createReturnResult(value)); 138 lastInvocationUsed = true; 139 } 140 141 @SuppressWarnings("deprecation") setDefaultReturnValue(Object value)142 public void setDefaultReturnValue(Object value) { 143 requireMethodCall("default return value"); 144 value = convertNumberClassIfNeccessary(value); 145 requireAssignable(value); 146 if (lastResult != null) { 147 times(MocksControl.ONCE); 148 } 149 behavior.addStub( 150 lastInvocation.withMatcher(org.easymock.MockControl.ALWAYS_MATCHER), Result 151 .createReturnResult(value)); 152 lastInvocationUsed = true; 153 } 154 asStub()155 public void asStub() { 156 requireMethodCall("stub behavior"); 157 requireVoidMethod(); 158 behavior.addStub(lastInvocation, Result.createReturnResult(null)); 159 lastInvocationUsed = true; 160 } 161 162 @SuppressWarnings("deprecation") setDefaultVoidCallable()163 public void setDefaultVoidCallable() { 164 requireMethodCall("default void callable"); 165 requireVoidMethod(); 166 behavior.addStub( 167 lastInvocation.withMatcher(org.easymock.MockControl.ALWAYS_MATCHER), Result 168 .createReturnResult(null)); 169 lastInvocationUsed = true; 170 } 171 andStubThrow(Throwable throwable)172 public void andStubThrow(Throwable throwable) { 173 requireMethodCall("stub Throwable"); 174 requireValidThrowable(throwable); 175 if (lastResult != null) { 176 times(MocksControl.ONCE); 177 } 178 behavior.addStub(lastInvocation, Result.createThrowResult(throwable)); 179 lastInvocationUsed = true; 180 } 181 182 @SuppressWarnings("deprecation") setDefaultThrowable(Throwable throwable)183 public void setDefaultThrowable(Throwable throwable) { 184 requireMethodCall("default Throwable"); 185 requireValidThrowable(throwable); 186 if (lastResult != null) { 187 times(MocksControl.ONCE); 188 } 189 behavior.addStub( 190 lastInvocation.withMatcher(org.easymock.MockControl.ALWAYS_MATCHER), Result 191 .createThrowResult(throwable)); 192 lastInvocationUsed = true; 193 } 194 andStubAnswer(IAnswer<?> answer)195 public void andStubAnswer(IAnswer<?> answer) { 196 requireMethodCall("stub answer"); 197 requireValidAnswer(answer); 198 if (lastResult != null) { 199 times(MocksControl.ONCE); 200 } 201 behavior.addStub(lastInvocation, Result.createAnswerResult(answer)); 202 lastInvocationUsed = true; 203 } 204 andStubDelegateTo(Object delegateTo)205 public void andStubDelegateTo(Object delegateTo) { 206 requireMethodCall("stub delegate"); 207 requireValidDelegation(delegateTo); 208 if (lastResult != null) { 209 times(MocksControl.ONCE); 210 } 211 behavior.addStub(lastInvocation, Result 212 .createDelegatingResult(delegateTo)); 213 lastInvocationUsed = true; 214 } 215 times(Range range)216 public void times(Range range) { 217 requireMethodCall("times"); 218 requireLastResultOrVoidMethod(); 219 220 behavior.addExpected(lastInvocation, lastResult != null ? lastResult 221 : Result.createReturnResult(null), range); 222 lastInvocationUsed = true; 223 lastResult = null; 224 } 225 createNumberObject(Object value, Class<?> returnType)226 private Object createNumberObject(Object value, Class<?> returnType) { 227 if (!(value instanceof Number)) { 228 return value; 229 } 230 Number number = (Number) value; 231 if (returnType.equals(Byte.TYPE)) { 232 return number.byteValue(); 233 } else if (returnType.equals(Short.TYPE)) { 234 return number.shortValue(); 235 } else if (returnType.equals(Character.TYPE)) { 236 return (char) number.intValue(); 237 } else if (returnType.equals(Integer.TYPE)) { 238 return number.intValue(); 239 } else if (returnType.equals(Long.TYPE)) { 240 return number.longValue(); 241 } else if (returnType.equals(Float.TYPE)) { 242 return number.floatValue(); 243 } else if (returnType.equals(Double.TYPE)) { 244 return number.doubleValue(); 245 } else { 246 return number; 247 } 248 } 249 convertNumberClassIfNeccessary(Object o)250 private Object convertNumberClassIfNeccessary(Object o) { 251 Class<?> returnType = lastInvocation.getMethod().getReturnType(); 252 return createNumberObject(o, returnType); 253 } 254 255 @SuppressWarnings("deprecation") closeMethod()256 private void closeMethod() { 257 if (lastInvocationUsed && lastResult == null) { 258 return; 259 } 260 if (!isLastResultOrVoidMethod()) { 261 throw new RuntimeExceptionWrapper(new IllegalStateException( 262 "missing behavior definition for the preceding method call " 263 + lastInvocation.toString())); 264 } 265 this.times(org.easymock.MockControl.ONE); 266 } 267 emptyReturnValueFor(Class<?> type)268 public static Object emptyReturnValueFor(Class<?> type) { 269 return type.isPrimitive() ? emptyReturnValues.get(type) : null; 270 } 271 requireMethodCall(String failMessage)272 private void requireMethodCall(String failMessage) { 273 if (lastInvocation == null) { 274 throw new RuntimeExceptionWrapper(new IllegalStateException( 275 "method call on the mock needed before setting " 276 + failMessage)); 277 } 278 } 279 requireAssignable(Object returnValue)280 private void requireAssignable(Object returnValue) { 281 if (lastMethodIsVoidMethod()) { 282 throw new RuntimeExceptionWrapper(new IllegalStateException( 283 "void method cannot return a value")); 284 } 285 if (returnValue == null) { 286 return; 287 } 288 Class<?> returnedType = lastInvocation.getMethod().getReturnType(); 289 if (returnedType.isPrimitive()) { 290 returnedType = primitiveToWrapperType.get(returnedType); 291 292 } 293 if (!returnedType.isAssignableFrom(returnValue.getClass())) { 294 throw new RuntimeExceptionWrapper(new IllegalStateException( 295 "incompatible return value type")); 296 } 297 } 298 requireValidThrowable(Throwable throwable)299 private void requireValidThrowable(Throwable throwable) { 300 if (throwable == null) 301 throw new RuntimeExceptionWrapper(new NullPointerException( 302 "null cannot be thrown")); 303 if (isValidThrowable(throwable)) 304 return; 305 306 throw new RuntimeExceptionWrapper(new IllegalArgumentException( 307 "last method called on mock cannot throw " 308 + throwable.getClass().getName())); 309 } 310 requireValidAnswer(IAnswer<?> answer)311 private void requireValidAnswer(IAnswer<?> answer) { 312 if (answer == null) 313 throw new RuntimeExceptionWrapper(new NullPointerException( 314 "answer object must not be null")); 315 } 316 requireValidDelegation(Object delegateTo)317 private void requireValidDelegation(Object delegateTo) { 318 if (delegateTo == null) 319 throw new RuntimeExceptionWrapper(new NullPointerException( 320 "delegated to object must not be null")); 321 // Would be nice to validate delegateTo is implementing the mock 322 // interface (not possible right now) 323 } 324 requireLastResultOrVoidMethod()325 private void requireLastResultOrVoidMethod() { 326 if (isLastResultOrVoidMethod()) { 327 return; 328 } 329 throw new RuntimeExceptionWrapper(new IllegalStateException( 330 "last method called on mock is not a void method")); 331 } 332 requireVoidMethod()333 private void requireVoidMethod() { 334 if (lastMethodIsVoidMethod()) { 335 return; 336 } 337 throw new RuntimeExceptionWrapper(new IllegalStateException( 338 "last method called on mock is not a void method")); 339 } 340 isLastResultOrVoidMethod()341 private boolean isLastResultOrVoidMethod() { 342 return lastResult != null || lastMethodIsVoidMethod(); 343 } 344 lastMethodIsVoidMethod()345 private boolean lastMethodIsVoidMethod() { 346 Class<?> returnType = lastInvocation.getMethod().getReturnType(); 347 return returnType.equals(Void.TYPE); 348 } 349 isValidThrowable(Throwable throwable)350 private boolean isValidThrowable(Throwable throwable) { 351 if (throwable instanceof RuntimeException) { 352 return true; 353 } 354 if (throwable instanceof Error) { 355 return true; 356 } 357 Class<?>[] exceptions = lastInvocation.getMethod().getExceptionTypes(); 358 Class<?> throwableClass = throwable.getClass(); 359 for (Class<?> exception : exceptions) { 360 if (exception.isAssignableFrom(throwableClass)) 361 return true; 362 } 363 return false; 364 } 365 checkOrder(boolean value)366 public void checkOrder(boolean value) { 367 closeMethod(); 368 behavior.checkOrder(value); 369 } 370 makeThreadSafe(boolean threadSafe)371 public void makeThreadSafe(boolean threadSafe) { 372 behavior.makeThreadSafe(threadSafe); 373 } 374 checkIsUsedInOneThread(boolean shouldBeUsedInOneThread)375 public void checkIsUsedInOneThread(boolean shouldBeUsedInOneThread) { 376 behavior.shouldBeUsedInOneThread(shouldBeUsedInOneThread); 377 } 378 379 @SuppressWarnings("deprecation") setDefaultMatcher(org.easymock.ArgumentsMatcher matcher)380 public void setDefaultMatcher(org.easymock.ArgumentsMatcher matcher) { 381 behavior.setDefaultMatcher(matcher); 382 } 383 384 @SuppressWarnings("deprecation") setMatcher(Method method, org.easymock.ArgumentsMatcher matcher)385 public void setMatcher(Method method, org.easymock.ArgumentsMatcher matcher) { 386 requireMethodCall("matcher"); 387 behavior.setMatcher(lastInvocation.getMethod(), matcher); 388 } 389 }