/* * Copyright (c) 2006 Rogério Liesenfeld * This file is subject to the terms of the MIT license (see LICENSE.txt). */ package mockit.internal.expectations.invocation; import java.lang.reflect.*; import java.util.*; import javax.annotation.*; import mockit.external.asm.*; import mockit.internal.*; import mockit.internal.expectations.argumentMatching.*; import mockit.internal.reflection.*; import mockit.internal.state.*; import mockit.internal.util.*; public final class InvocationArguments { @Nonnull final String classDesc; @Nonnull final String methodNameAndDesc; @Nullable final String genericSignature; @Nonnull private final ArgumentValuesAndMatchers valuesAndMatchers; @Nullable private Member realMethodOrConstructor; InvocationArguments( int access, @Nonnull String classDesc, @Nonnull String methodNameAndDesc, @Nullable String genericSignature, @Nonnull Object[] args) { this.classDesc = classDesc; this.methodNameAndDesc = methodNameAndDesc; this.genericSignature = genericSignature; valuesAndMatchers = (access & Opcodes.ACC_VARARGS) == 0 ? new ArgumentValuesAndMatchersWithoutVarargs(this, args) : new ArgumentValuesAndMatchersWithVarargs(this, args); } @Nonnull String getClassName() { return classDesc.replace('/', '.'); } boolean isForConstructor() { return methodNameAndDesc.charAt(0) == '<'; } @Nonnull public Object[] getValues() { return valuesAndMatchers.values; } void setValues(@Nonnull Object[] values) { valuesAndMatchers.values = values; } public void setValuesWithNoMatchers(@Nonnull Object[] argsToVerify) { valuesAndMatchers.setValuesWithNoMatchers(argsToVerify); } public void setValuesAndMatchers(@Nonnull Object[] argsToVerify, @Nullable List<ArgumentMatcher<?>> matchers) { valuesAndMatchers.setValuesAndMatchers(argsToVerify, matchers); } @Nullable public List<ArgumentMatcher<?>> getMatchers() { return valuesAndMatchers.matchers; } public void setMatchers(@Nullable List<ArgumentMatcher<?>> matchers) { valuesAndMatchers.matchers = matchers; } @Nonnull public Object[] prepareForVerification(@Nonnull Object[] argsToVerify, @Nullable List<ArgumentMatcher<?>> matchers) { return valuesAndMatchers.prepareForVerification(argsToVerify, matchers); } public boolean isMatch(@Nonnull Object[] replayArgs, @Nonnull Map<Object, Object> instanceMap) { TestRun.enterNoMockingZone(); ExecutingTest executingTest = TestRun.getExecutingTest(); boolean previousFlag = executingTest.setShouldIgnoreMockingCallbacks(true); try { return valuesAndMatchers.isMatch(replayArgs, instanceMap); } finally { executingTest.setShouldIgnoreMockingCallbacks(previousFlag); TestRun.exitNoMockingZone(); } } @Nullable public Error assertMatch(@Nonnull Object[] replayArgs, @Nonnull Map<Object, Object> instanceMap) { return valuesAndMatchers.assertMatch(replayArgs, instanceMap); } @Nonnull Error argumentMismatchMessage(int paramIndex, @Nullable Object expected, @Nullable Object actual) { ArgumentMismatch message = new ArgumentMismatch(); message.append("Parameter "); String parameterName = ParameterNames.getName(classDesc, methodNameAndDesc, paramIndex); if (parameterName == null) { message.append(paramIndex); } else { message.appendFormatted(parameterName); } message.append(" of ").append(new MethodFormatter(classDesc, methodNameAndDesc).toString()); message.append(" expected ").appendFormatted(expected); if (!message.isFinished()) { message.append(", got ").appendFormatted(actual); if (actual != null) { appendWarningMessageAboutLackOfEqualsMethod(message, actual); } } return new UnexpectedInvocation(message.toString()); } private static void appendWarningMessageAboutLackOfEqualsMethod( @Nonnull ArgumentMismatch message, @Nonnull Object value) { Class<?> argClass = value.getClass(); if ( argClass == String.class || argClass == Boolean.class || argClass == Character.class || Number.class.isAssignableFrom(argClass) ) { return; } Method equalsMethod; try { equalsMethod = argClass.getMethod("equals", Object.class); } catch (NoSuchMethodException e) { throw new RuntimeException(e); } if (equalsMethod.getDeclaringClass() == Object.class) { message.append("\n Warning: argument class ").append(argClass.getName()).append(" has no \"equals\" method"); } } @Override public String toString() { MethodFormatter methodFormatter = new MethodFormatter(classDesc, methodNameAndDesc, false); List<String> parameterTypes = methodFormatter.getParameterTypes(); String arguments = valuesAndMatchers.toString(parameterTypes); methodFormatter.append(arguments); return methodFormatter.toString(); } public boolean hasEquivalentMatchers(@Nonnull InvocationArguments other) { return valuesAndMatchers.hasEquivalentMatchers(other.valuesAndMatchers); } @Nonnull Member getRealMethodOrConstructor() { if (realMethodOrConstructor == null) { try { realMethodOrConstructor = new RealMethodOrConstructor(getClassName(), methodNameAndDesc).getMember(); } catch (NoSuchMethodException e) { throw new RuntimeException(e); } } return realMethodOrConstructor; } }