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