• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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