blob: 6d22100dc33c7ed066e2becb4e33cc44e302885a [file] [log] [blame]
package org.robolectric;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
import org.robolectric.annotation.internal.Instrument;
import org.robolectric.internal.SandboxTestRunner;
import org.robolectric.internal.bytecode.SandboxConfig;
import org.robolectric.internal.bytecode.ShadowConstants;
import org.robolectric.shadow.api.Shadow;
import org.robolectric.testing.AnUninstrumentedClass;
import org.robolectric.testing.Pony;
@RunWith(SandboxTestRunner.class)
public class ShadowingTest {
@Test
@SandboxConfig(shadows = {ShadowAccountManagerForTests.class})
public void testStaticMethodsAreDelegated() throws Exception {
Object arg = mock(Object.class);
AccountManager.get(arg);
assertThat(ShadowAccountManagerForTests.wasCalled).isTrue();
assertThat(ShadowAccountManagerForTests.arg).isSameInstanceAs(arg);
}
@Implements(AccountManager.class)
public static class ShadowAccountManagerForTests {
public static boolean wasCalled = false;
public static Object arg;
public static AccountManager get(Object arg) {
wasCalled = true;
ShadowAccountManagerForTests.arg = arg;
return mock(AccountManager.class);
}
}
static class Context {
}
static class AccountManager {
public static AccountManager get(Object arg) {
return null;
}
}
@Test
@SandboxConfig(shadows = {ShadowClassWithProtectedMethod.class})
public void testProtectedMethodsAreDelegated() throws Exception {
ClassWithProtectedMethod overlay = new ClassWithProtectedMethod();
assertEquals("shadow name", overlay.getName());
}
@Implements(ClassWithProtectedMethod.class)
public static class ShadowClassWithProtectedMethod {
@Implementation
protected String getName() {
return "shadow name";
}
}
@Instrument
public static class ClassWithProtectedMethod {
protected String getName() {
return "protected name";
}
}
@Test
@SandboxConfig(shadows = {ShadowPaintForTests.class})
public void testNativeMethodsAreDelegated() throws Exception {
Paint paint = new Paint();
paint.setColor(1234);
assertThat(paint.getColor()).isEqualTo(1234);
}
@Instrument
static class Paint {
public native void setColor(int color);
public native int getColor();
}
@Implements(Paint.class)
public static class ShadowPaintForTests {
private int color;
@Implementation
protected void setColor(int color) {
this.color = color;
}
@Implementation
protected int getColor() {
return color;
}
}
@Implements(ClassWithNoDefaultConstructor.class)
public static class ShadowForClassWithNoDefaultConstructor {
public static boolean shadowDefaultConstructorCalled = false;
public static boolean shadowDefaultConstructorImplementorCalled = false;
public ShadowForClassWithNoDefaultConstructor() {
shadowDefaultConstructorCalled = true;
}
@Implementation
protected void __constructor__() {
shadowDefaultConstructorImplementorCalled = true;
}
}
@Instrument @SuppressWarnings({"UnusedDeclaration"})
public static class ClassWithNoDefaultConstructor {
ClassWithNoDefaultConstructor(String string) {
}
}
@Test
@SandboxConfig(shadows = {Pony.ShadowPony.class})
public void directlyOn_shouldCallThroughToOriginalMethodBody() throws Exception {
Pony pony = new Pony();
assertEquals("Fake whinny! You're on my neck!", pony.ride("neck"));
assertEquals("Whinny! You're on my neck!", Shadow.directlyOn(pony, Pony.class).ride("neck"));
assertEquals("Fake whinny! You're on my haunches!", pony.ride("haunches"));
}
@Test
@SandboxConfig(shadows = {Pony.ShadowPony.class})
public void shouldCallRealForUnshadowedMethod() throws Exception {
assertEquals("Off I saunter to the salon!", new Pony().saunter("the salon"));
}
static class TextView {
}
static class ColorStateList {
public ColorStateList(int[][] ints, int[] ints1) {
}
}
static class TypedArray {
}
@Implements(TextView.class)
public static class TextViewWithDummyGetTextColorsMethod {
public static ColorStateList getTextColors(Context context, TypedArray attrs) {
return new ColorStateList(new int[0][0], new int[0]);
}
}
@Test
@SandboxConfig(shadows = ShadowOfClassWithSomeConstructors.class)
public void shouldGenerateSeparatedConstructorBodies() throws Exception {
ClassWithSomeConstructors o = new ClassWithSomeConstructors("my name");
assertNull(o.name);
Method realConstructor = o.getClass().getDeclaredMethod(ShadowConstants.CONSTRUCTOR_METHOD_NAME, String.class);
realConstructor.setAccessible(true);
realConstructor.invoke(o, "my name");
assertEquals("my name", o.name);
}
@Instrument
public static class ClassWithSomeConstructors {
public String name;
public ClassWithSomeConstructors(String name) {
this.name = name;
}
}
@Implements(ClassWithSomeConstructors.class)
public static class ShadowOfClassWithSomeConstructors {
@Implementation
protected void __constructor__(String s) {
}
}
@Test
@SandboxConfig(shadows = {ShadowApiImplementedClass.class})
public void withNonApiSubclassesWhichExtendApi_shouldStillBeInvoked() throws Exception {
assertEquals("did foo", new NonApiSubclass().doSomething("foo"));
}
public static class NonApiSubclass extends ApiImplementedClass {
public String doSomething(String value) {
return "did " + value;
}
}
@Instrument
public static class ApiImplementedClass {
}
@Implements(ApiImplementedClass.class)
public static class ShadowApiImplementedClass {
}
@Test
public void shouldNotInstrumentClassIfNotAddedToConfig() {
assertEquals(1, new NonInstrumentedClass().plus(0));
}
@Test
@SandboxConfig(shadows = {ShadowNonInstrumentedClass.class})
public void shouldInstrumentClassIfAddedToConfig() {
assertEquals(2, new NonInstrumentedClass().plus(0));
}
public static class NonInstrumentedClass {
public int plus(int x) {
return x + 1;
}
}
@Implements(NonInstrumentedClass.class)
public static class ShadowNonInstrumentedClass {
@Implementation
protected int plus(int x) {
return x + 2;
}
}
@Test
public void shouldNotInstrumentPackageIfNotAddedToConfig() throws Exception {
Class<?> clazz = Class.forName(AnUninstrumentedClass.class.getName());
assertTrue(Modifier.isFinal(clazz.getModifiers()));
}
@Test
@SandboxConfig(instrumentedPackages = {"org.robolectric.testing"})
public void shouldInstrumentPackageIfAddedToConfig() throws Exception {
Class<?> clazz = Class.forName(AnUninstrumentedClass.class.getName());
assertFalse(Modifier.isFinal(clazz.getModifiers()));
}
}