1 package org.robolectric; 2 3 import static com.google.common.truth.Truth.assertThat; 4 import static org.junit.Assert.assertEquals; 5 import static org.junit.Assert.assertFalse; 6 import static org.junit.Assert.assertNull; 7 import static org.junit.Assert.assertTrue; 8 import static org.mockito.Mockito.mock; 9 10 import java.lang.reflect.Method; 11 import java.lang.reflect.Modifier; 12 import org.junit.Test; 13 import org.junit.runner.RunWith; 14 import org.robolectric.annotation.Implementation; 15 import org.robolectric.annotation.Implements; 16 import org.robolectric.annotation.internal.Instrument; 17 import org.robolectric.internal.SandboxTestRunner; 18 import org.robolectric.internal.bytecode.SandboxConfig; 19 import org.robolectric.internal.bytecode.ShadowConstants; 20 import org.robolectric.shadow.api.Shadow; 21 import org.robolectric.testing.AnUninstrumentedClass; 22 import org.robolectric.testing.Pony; 23 24 @RunWith(SandboxTestRunner.class) 25 public class ShadowingTest { 26 27 @Test 28 @SandboxConfig(shadows = {ShadowAccountManagerForTests.class}) testStaticMethodsAreDelegated()29 public void testStaticMethodsAreDelegated() throws Exception { 30 Object arg = mock(Object.class); 31 AccountManager.get(arg); 32 assertThat(ShadowAccountManagerForTests.wasCalled).isTrue(); 33 assertThat(ShadowAccountManagerForTests.arg).isSameInstanceAs(arg); 34 } 35 36 @Implements(AccountManager.class) 37 public static class ShadowAccountManagerForTests { 38 public static boolean wasCalled = false; 39 public static Object arg; 40 get(Object arg)41 public static AccountManager get(Object arg) { 42 wasCalled = true; 43 ShadowAccountManagerForTests.arg = arg; 44 return mock(AccountManager.class); 45 } 46 } 47 48 static class Context {} 49 50 static class AccountManager { get(Object arg)51 public static AccountManager get(Object arg) { 52 return null; 53 } 54 } 55 56 @Test 57 @SandboxConfig(shadows = {ShadowClassWithProtectedMethod.class}) testProtectedMethodsAreDelegated()58 public void testProtectedMethodsAreDelegated() throws Exception { 59 ClassWithProtectedMethod overlay = new ClassWithProtectedMethod(); 60 assertEquals("shadow name", overlay.getName()); 61 } 62 63 @Implements(ClassWithProtectedMethod.class) 64 public static class ShadowClassWithProtectedMethod { 65 @Implementation getName()66 protected String getName() { 67 return "shadow name"; 68 } 69 } 70 71 @Instrument 72 public static class ClassWithProtectedMethod { getName()73 protected String getName() { 74 return "protected name"; 75 } 76 } 77 78 @Test 79 @SandboxConfig(shadows = {ShadowPaintForTests.class}) testNativeMethodsAreDelegated()80 public void testNativeMethodsAreDelegated() throws Exception { 81 Paint paint = new Paint(); 82 paint.setColor(1234); 83 84 assertThat(paint.getColor()).isEqualTo(1234); 85 } 86 87 @Instrument 88 static class Paint { setColor(int color)89 public native void setColor(int color); 90 getColor()91 public native int getColor(); 92 } 93 94 @Implements(Paint.class) 95 public static class ShadowPaintForTests { 96 private int color; 97 98 @Implementation setColor(int color)99 protected void setColor(int color) { 100 this.color = color; 101 } 102 103 @Implementation getColor()104 protected int getColor() { 105 return color; 106 } 107 } 108 109 @Implements(ClassWithNoDefaultConstructor.class) 110 public static class ShadowForClassWithNoDefaultConstructor { 111 public static boolean shadowDefaultConstructorCalled = false; 112 public static boolean shadowDefaultConstructorImplementorCalled = false; 113 ShadowForClassWithNoDefaultConstructor()114 public ShadowForClassWithNoDefaultConstructor() { 115 shadowDefaultConstructorCalled = true; 116 } 117 118 @Implementation __constructor__()119 protected void __constructor__() { 120 shadowDefaultConstructorImplementorCalled = true; 121 } 122 } 123 124 @Instrument 125 @SuppressWarnings({"UnusedDeclaration"}) 126 public static class ClassWithNoDefaultConstructor { ClassWithNoDefaultConstructor(String string)127 ClassWithNoDefaultConstructor(String string) {} 128 } 129 130 @Test 131 @SandboxConfig(shadows = {Pony.ShadowPony.class}) directlyOn_shouldCallThroughToOriginalMethodBody()132 public void directlyOn_shouldCallThroughToOriginalMethodBody() throws Exception { 133 Pony pony = new Pony(); 134 135 assertEquals("Fake whinny! You're on my neck!", pony.ride("neck")); 136 assertEquals("Whinny! You're on my neck!", Shadow.directlyOn(pony, Pony.class).ride("neck")); 137 138 assertEquals("Fake whinny! You're on my haunches!", pony.ride("haunches")); 139 } 140 141 @Test 142 @SandboxConfig(shadows = {Pony.ShadowPony.class}) shouldCallRealForUnshadowedMethod()143 public void shouldCallRealForUnshadowedMethod() throws Exception { 144 assertEquals("Off I saunter to the salon!", new Pony().saunter("the salon")); 145 } 146 147 static class TextView {} 148 149 static class ColorStateList { ColorStateList(int[][] ints, int[] ints1)150 public ColorStateList(int[][] ints, int[] ints1) {} 151 } 152 153 static class TypedArray {} 154 155 @Implements(TextView.class) 156 public static class TextViewWithDummyGetTextColorsMethod { getTextColors(Context context, TypedArray attrs)157 public static ColorStateList getTextColors(Context context, TypedArray attrs) { 158 return new ColorStateList(new int[0][0], new int[0]); 159 } 160 } 161 162 @Test 163 @SandboxConfig(shadows = ShadowOfClassWithSomeConstructors.class) shouldGenerateSeparatedConstructorBodies()164 public void shouldGenerateSeparatedConstructorBodies() throws Exception { 165 ClassWithSomeConstructors o = new ClassWithSomeConstructors("my name"); 166 assertNull(o.name); 167 168 Method realConstructor = 169 o.getClass().getDeclaredMethod(ShadowConstants.CONSTRUCTOR_METHOD_NAME, String.class); 170 realConstructor.setAccessible(true); 171 realConstructor.invoke(o, "my name"); 172 assertEquals("my name", o.name); 173 } 174 175 @Instrument 176 public static class ClassWithSomeConstructors { 177 public String name; 178 ClassWithSomeConstructors(String name)179 public ClassWithSomeConstructors(String name) { 180 this.name = name; 181 } 182 } 183 184 @Implements(ClassWithSomeConstructors.class) 185 public static class ShadowOfClassWithSomeConstructors { 186 @Implementation __constructor__(String s)187 protected void __constructor__(String s) {} 188 } 189 190 @Test 191 @SandboxConfig(shadows = {ShadowApiImplementedClass.class}) withNonApiSubclassesWhichExtendApi_shouldStillBeInvoked()192 public void withNonApiSubclassesWhichExtendApi_shouldStillBeInvoked() throws Exception { 193 assertEquals("did foo", new NonApiSubclass().doSomething("foo")); 194 } 195 196 public static class NonApiSubclass extends ApiImplementedClass { doSomething(String value)197 public String doSomething(String value) { 198 return "did " + value; 199 } 200 } 201 202 @Instrument 203 public static class ApiImplementedClass {} 204 205 @Implements(ApiImplementedClass.class) 206 public static class ShadowApiImplementedClass {} 207 208 @Test shouldNotInstrumentClassIfNotAddedToConfig()209 public void shouldNotInstrumentClassIfNotAddedToConfig() { 210 assertEquals(1, new NonInstrumentedClass().plus(0)); 211 } 212 213 @Test 214 @SandboxConfig(shadows = {ShadowNonInstrumentedClass.class}) shouldInstrumentClassIfAddedToConfig()215 public void shouldInstrumentClassIfAddedToConfig() { 216 assertEquals(2, new NonInstrumentedClass().plus(0)); 217 } 218 219 public static class NonInstrumentedClass { plus(int x)220 public int plus(int x) { 221 return x + 1; 222 } 223 } 224 225 @Implements(NonInstrumentedClass.class) 226 public static class ShadowNonInstrumentedClass { 227 @Implementation plus(int x)228 protected int plus(int x) { 229 return x + 2; 230 } 231 } 232 233 @Test shouldNotInstrumentPackageIfNotAddedToConfig()234 public void shouldNotInstrumentPackageIfNotAddedToConfig() throws Exception { 235 Class<?> clazz = Class.forName(AnUninstrumentedClass.class.getName()); 236 assertTrue(Modifier.isFinal(clazz.getModifiers())); 237 } 238 239 @Test 240 @SandboxConfig(instrumentedPackages = {"org.robolectric.testing"}) shouldInstrumentPackageIfAddedToConfig()241 public void shouldInstrumentPackageIfAddedToConfig() throws Exception { 242 Class<?> clazz = Class.forName(AnUninstrumentedClass.class.getName()); 243 assertFalse(Modifier.isFinal(clazz.getModifiers())); 244 } 245 } 246