package org.robolectric.internal.bytecode;
import org.junit.Before;
import org.junit.Test;
import org.robolectric.android.AndroidInterceptors;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
import org.robolectric.util.Function;
import java.util.LinkedHashMap;
import java.util.Map;
import static org.assertj.core.api.Assertions.assertThat;
@SuppressWarnings("unchecked")
public class ShadowWranglerUnitTest {
private ShadowWrangler shadowWrangler;
private Interceptors interceptors;
@Before
public void setup() throws Exception {
interceptors = new Interceptors(AndroidInterceptors.all());
shadowWrangler = new ShadowWrangler(ShadowMap.EMPTY, 23, interceptors);
}
@Test
public void getInterceptionHandler_whenCallIsNotRecognized_shouldReturnDoNothingHandler() throws Exception {
MethodSignature methodSignature = MethodSignature.parse("java/lang/Object/unknownMethod()V");
Function<Object,Object> handler = interceptors.getInterceptionHandler(methodSignature);
assertThat(handler.call(null, null, new Object[0])).isNull();
}
@Test
public void getInterceptionHandler_whenInterceptingElderOnLinkedHashMap_shouldReturnNonDoNothingHandler() throws Exception {
MethodSignature methodSignature = MethodSignature.parse("java/util/LinkedHashMap/eldest()Ljava/lang/Object;");
Function<Object,Object> handler = interceptors.getInterceptionHandler(methodSignature);
assertThat(handler).isNotSameAs(ShadowWrangler.DO_NOTHING_HANDLER);
}
@Test
public void intercept_elderOnLinkedHashMapHandler_shouldReturnEldestMemberOfLinkedHashMap() throws Throwable {
LinkedHashMap<Integer, String> map = new LinkedHashMap<>(2);
map.put(1, "one");
map.put(2, "two");
Map.Entry<Integer, String> result = (Map.Entry<Integer, String>)
shadowWrangler.intercept("java/util/LinkedHashMap/eldest()Ljava/lang/Object;", map, null, getClass());
Map.Entry<Integer, String> eldestMember = map.entrySet().iterator().next();
assertThat(result).isEqualTo(eldestMember);
assertThat(result.getKey()).isEqualTo(1);
assertThat(result.getValue()).isEqualTo("one");
}
@Test
public void intercept_elderOnLinkedHashMapHandler_shouldReturnNullForEmptyMap() throws Throwable {
LinkedHashMap<Integer, String> map = new LinkedHashMap<>();
Map.Entry<Integer, String> result = (Map.Entry<Integer, String>)
shadowWrangler.intercept("java/util/LinkedHashMap/eldest()Ljava/lang/Object;", map, null, getClass());
assertThat(result).isNull();
}
@Test
public void shadowClassWithSdkRange() throws Throwable {
ShadowMap shadowMap = new ShadowMap.Builder().addShadowClasses(ShadowDummyClass.class).build();
String methodName = internalName(DummyClass.class) + "/methodWithoutRange()V";
assertThat(new ShadowWrangler(shadowMap, 18, interceptors).methodInvoked(methodName, false, DummyClass.class)).isNull();
assertThat(new ShadowWrangler(shadowMap, 19, interceptors).methodInvoked(methodName, false, DummyClass.class).describe())
.contains("ShadowDummyClass.methodWithoutRange()");
assertThat(new ShadowWrangler(shadowMap, 23, interceptors).methodInvoked(methodName, false, DummyClass.class)).isNull();
}
@Test
public void shadowMethodWithSdkRange() throws Throwable {
ShadowMap shadowMap = new ShadowMap.Builder().addShadowClasses(ShadowDummyClass.class).build();
String methodName = internalName(DummyClass.class) + "/methodFor20()V";
assertThat(new ShadowWrangler(shadowMap, 19, interceptors).methodInvoked(methodName, false, DummyClass.class)).isNull();
assertThat(new ShadowWrangler(shadowMap, 20, interceptors).methodInvoked(methodName, false, DummyClass.class).describe())
.contains("ShadowDummyClass.methodFor20()");
assertThat(new ShadowWrangler(shadowMap, 21, interceptors).methodInvoked(methodName, false, DummyClass.class)).isNull();
}
@Test
public void shadowMethodWithMinSdk() throws Throwable {
ShadowMap shadowMap = new ShadowMap.Builder().addShadowClasses(ShadowDummyClass.class).build();
String methodName = internalName(DummyClass.class) + "/methodMin20()V";
assertThat(new ShadowWrangler(shadowMap, 19, interceptors).methodInvoked(methodName, false, DummyClass.class)).isNull();
assertThat(new ShadowWrangler(shadowMap, 20, interceptors).methodInvoked(methodName, false, DummyClass.class).describe())
.contains("ShadowDummyClass.methodMin20()");
assertThat(new ShadowWrangler(shadowMap, 21, interceptors).methodInvoked(methodName, false, DummyClass.class).describe())
.contains("ShadowDummyClass.methodMin20()");
}
@Test
public void shadowMethodWithMaxSdk() throws Throwable {
ShadowMap shadowMap = new ShadowMap.Builder().addShadowClasses(ShadowDummyClass.class).build();
String methodName = internalName(DummyClass.class) + "/methodMax20()V";
assertThat(new ShadowWrangler(shadowMap, 19, interceptors).methodInvoked(methodName, false, DummyClass.class).describe())
.contains("ShadowDummyClass.methodMax20()");
assertThat(new ShadowWrangler(shadowMap, 20, interceptors).methodInvoked(methodName, false, DummyClass.class).describe())
.contains("ShadowDummyClass.methodMax20()");
assertThat(new ShadowWrangler(shadowMap, 21, interceptors).methodInvoked(methodName, false, DummyClass.class)).isNull();
}
@Test
public void shadowConstructor() throws Throwable {
ShadowMap shadowMap = new ShadowMap.Builder().addShadowClasses(ShadowDummyClass.class).build();
String methodName = internalName(DummyClass.class) + "/__constructor__()V";
assertThat(new ShadowWrangler(shadowMap, 19, interceptors).methodInvoked(methodName, false, DummyClass.class)).isNull();
assertThat(new ShadowWrangler(shadowMap, 20, interceptors).methodInvoked(methodName, false, DummyClass.class).describe())
.contains("ShadowDummyClass.__constructor__()");
assertThat(new ShadowWrangler(shadowMap, 21, interceptors).methodInvoked(methodName, false, DummyClass.class)).isNull();
}
@Test
public void whenChildShadowHasNarrowerSdk_createShadowFor_shouldReturnSuperShadowSometimes() throws Exception {
ShadowMap shadowMap = new ShadowMap.Builder().addShadowClasses(ShadowDummyClass.class, ShadowChildOfDummyClass.class).build();
assertThat(new ShadowWrangler(shadowMap, 18, interceptors).createShadowFor(new ChildOfDummyClass()))
.isSameAs(ShadowWrangler.NO_SHADOW);
assertThat(new ShadowWrangler(shadowMap, 19, interceptors).createShadowFor(new ChildOfDummyClass()))
.isInstanceOf(ShadowDummyClass.class);
assertThat(new ShadowWrangler(shadowMap, 20, interceptors).createShadowFor(new ChildOfDummyClass()))
.isInstanceOf(ShadowChildOfDummyClass.class);
assertThat(new ShadowWrangler(shadowMap, 21, interceptors).createShadowFor(new ChildOfDummyClass()))
.isInstanceOf(ShadowChildOfDummyClass.class);
assertThat(new ShadowWrangler(shadowMap, 22, interceptors).createShadowFor(new ChildOfDummyClass()))
.isSameAs(ShadowWrangler.NO_SHADOW);
}
@Test
public void whenChildShadowHasNarrowerSdk_shouldCallAppropriateShadowMethod() throws Exception {
ShadowMap shadowMap = new ShadowMap.Builder().addShadowClasses(ShadowDummyClass.class, ShadowChildOfDummyClass.class).build();
String methodName = internalName(ChildOfDummyClass.class) + "/methodWithoutRange()V";
assertThat(new ShadowWrangler(shadowMap, 19, interceptors).methodInvoked(methodName, false, ChildOfDummyClass.class))
.isNull();
assertThat(new ShadowWrangler(shadowMap, 20, interceptors).methodInvoked(methodName, false, ChildOfDummyClass.class).describe())
.contains("ShadowChildOfDummyClass.methodWithoutRange()");
assertThat(new ShadowWrangler(shadowMap, 21, interceptors).methodInvoked(methodName, false, ChildOfDummyClass.class).describe())
.contains("ShadowChildOfDummyClass.methodWithoutRange()");
}
public static class DummyClass {
}
@Implements(value = DummyClass.class, minSdk = 19, maxSdk = 21)
public static class ShadowDummyClass {
@Implementation(minSdk = 20, maxSdk = 20)
public void __constructor__() {
}
@Implementation
public void methodWithoutRange() {
}
@Implementation(minSdk = 20, maxSdk = 20)
public void methodFor20() {
}
@Implementation(minSdk = 20)
public void methodMin20() {
}
@Implementation(maxSdk = 20)
public void methodMax20() {
}
}
public static class ChildOfDummyClass extends DummyClass {
}
@Implements(value = ChildOfDummyClass.class, minSdk = 20, maxSdk = 21)
public static class ShadowChildOfDummyClass {
@Implementation
public void methodWithoutRange() {
}
}
///////////////////////
private class WranglerBuilder extends ShadowMap.Builder {
ShadowWrangler wranglerFor(int apiLevel) {
return new ShadowWrangler(build(), apiLevel, interceptors);
}
@Override
public WranglerBuilder addShadowClasses(Class<?>... shadowClasses) {
return (WranglerBuilder) super.addShadowClasses(shadowClasses);
}
}
private String internalName(Class clazz) {
return clazz.getName().replaceAll("\\.", "/");
}
}