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 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); 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 @SuppressWarnings({"UnusedDeclaration"}) 125 public static class ClassWithNoDefaultConstructor { ClassWithNoDefaultConstructor(String string)126 ClassWithNoDefaultConstructor(String string) { 127 } 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 150 static class ColorStateList { ColorStateList(int[][] ints, int[] ints1)151 public ColorStateList(int[][] ints, int[] ints1) { 152 } 153 } 154 155 static class TypedArray { 156 } 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 = o.getClass().getDeclaredMethod(ShadowConstants.CONSTRUCTOR_METHOD_NAME, String.class); 172 realConstructor.setAccessible(true); 173 realConstructor.invoke(o, "my name"); 174 assertEquals("my name", o.name); 175 } 176 177 @Instrument 178 public static class ClassWithSomeConstructors { 179 public String name; 180 ClassWithSomeConstructors(String name)181 public ClassWithSomeConstructors(String name) { 182 this.name = name; 183 } 184 } 185 186 @Implements(ClassWithSomeConstructors.class) 187 public static class ShadowOfClassWithSomeConstructors { 188 @Implementation __constructor__(String s)189 protected void __constructor__(String s) { 190 } 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 209 @Implements(ApiImplementedClass.class) 210 public static class ShadowApiImplementedClass { 211 } 212 213 @Test shouldNotInstrumentClassIfNotAddedToConfig()214 public void shouldNotInstrumentClassIfNotAddedToConfig() { 215 assertEquals(1, new NonInstrumentedClass().plus(0)); 216 } 217 218 @Test 219 @SandboxConfig(shadows = {ShadowNonInstrumentedClass.class}) shouldInstrumentClassIfAddedToConfig()220 public void shouldInstrumentClassIfAddedToConfig() { 221 assertEquals(2, new NonInstrumentedClass().plus(0)); 222 } 223 224 public static class NonInstrumentedClass { plus(int x)225 public int plus(int x) { 226 return x + 1; 227 } 228 } 229 230 @Implements(NonInstrumentedClass.class) 231 public static class ShadowNonInstrumentedClass { 232 @Implementation plus(int x)233 protected int plus(int x) { 234 return x + 2; 235 } 236 } 237 238 @Test shouldNotInstrumentPackageIfNotAddedToConfig()239 public void shouldNotInstrumentPackageIfNotAddedToConfig() throws Exception { 240 Class<?> clazz = Class.forName(AnUninstrumentedClass.class.getName()); 241 assertTrue(Modifier.isFinal(clazz.getModifiers())); 242 } 243 244 @Test 245 @SandboxConfig(instrumentedPackages = {"org.robolectric.testing"}) shouldInstrumentPackageIfAddedToConfig()246 public void shouldInstrumentPackageIfAddedToConfig() throws Exception { 247 Class<?> clazz = Class.forName(AnUninstrumentedClass.class.getName()); 248 assertFalse(Modifier.isFinal(clazz.getModifiers())); 249 } 250 } 251