Skip to content

Commit

Permalink
Enhancement: added support for method overriding in generic interfaces;
Browse files Browse the repository at this point in the history
closes #231.
  • Loading branch information
rliesenfeld committed Oct 23, 2016
1 parent de42139 commit f6b6f06
Show file tree
Hide file tree
Showing 3 changed files with 60 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@

import mockit.external.asm.*;
import mockit.internal.*;
import mockit.internal.util.*;
import mockit.internal.util.GenericTypeReflection.*;
import static mockit.external.asm.Opcodes.*;

@SuppressWarnings("AbstractClassExtendsConcreteClass")
Expand Down Expand Up @@ -105,21 +107,42 @@ protected abstract void generateMethodBody(

protected final boolean isOverrideOfMethodFromSuperInterface(@Nonnull String name, @Nonnull String desc)
{
int p = desc.lastIndexOf(')');
String descNoReturnType = desc.substring(0, p + 1);

for (String implementedMethod : implementedMethods) {
if (
implementedMethod.startsWith(name) && implementedMethod.charAt(name.length()) == '(' &&
implementedMethod.contains(descNoReturnType)
) {
return true;
if (!implementedMethods.isEmpty()) {
int p = desc.lastIndexOf(')');
String descNoReturnType = desc.substring(0, p + 1);

for (String implementedMethod : implementedMethods) {
if (sameMethodName(implementedMethod, name) && implementedMethod.contains(descNoReturnType)) {
return true;
}
}
}

return false;
}

private static boolean sameMethodName(@Nonnull String implementedMethod, @Nonnull String name)
{
return implementedMethod.startsWith(name) && implementedMethod.charAt(name.length()) == '(';
}

@Nullable
protected final String getSubInterfaceOverride(
@Nonnull GenericTypeReflection genericTypeMap, @Nonnull String name, @Nonnull String genericSignature)
{
if (!implementedMethods.isEmpty()) {
GenericSignature parsedSignature = genericTypeMap.parseSignature(genericSignature);

for (String implementedMethod : implementedMethods) {
if (sameMethodName(implementedMethod, name) && parsedSignature.satisfiesSignature(implementedMethod)) {
return implementedMethod;
}
}
}

return null;
}

private final class MethodGeneratorForImplementedSuperInterface extends ClassVisitor
{
private String[] superInterfaces;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,23 @@ protected void generateMethodBody(
{
mw = cw.visitMethod(ACC_PUBLIC, name, desc, signature, exceptions);

String className = isOverrideOfMethodFromSuperInterface(name, desc) ? interfaceName : methodOwner;
generateDirectCallToHandler(mw, className, access, name, desc, signature);
String className = null;

if (signature != null) {
String subInterfaceOverride = getSubInterfaceOverride(mockedTypeInfo.genericTypeMap, name, signature);

if (subInterfaceOverride != null) {
className = interfaceName;
desc = subInterfaceOverride.substring(name.length());
signature = null;
}
}

if (className == null) {
className = isOverrideOfMethodFromSuperInterface(name, desc) ? interfaceName : methodOwner;
}

generateDirectCallToHandler(mw, className, access, name, desc, signature);
generateReturnWithObjectAtTopOfTheStack(desc);
mw.visitMaxs(1, 0);
}
Expand Down
12 changes: 12 additions & 0 deletions main/test/mockit/GenericMockedTypesTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -253,4 +253,16 @@ public void mockGenericTypeWithGenericMultiDimensionalArrayTypeArgument(@Mocked

assertEquals(0, result.length);
}

public interface BaseInterface<T> { void doSomething(T t); }
public interface SubInterface extends BaseInterface<String> { @Override void doSomething(String s); }

@Test
public void invokeGenericBaseInterfaceMethodThatIsOverriddenInMockedSubInterface(@Mocked final SubInterface mock)
{
@SuppressWarnings("UnnecessaryLocalVariable") BaseInterface<String> base = mock;
base.doSomething("test");

new Verifications() {{ mock.doSomething("test"); }};
}
}

0 comments on commit f6b6f06

Please sign in to comment.