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