xref: /aosp_15_r20/external/robolectric/sandbox/src/test/java/org/robolectric/ShadowingTest.java (revision e6ba16074e6af37d123cb567d575f496bf0a58ee)
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