1 package org.robolectric; 2 3 import static org.assertj.core.api.Assertions.assertThat; 4 import static org.junit.Assert.assertEquals; 5 import static org.junit.Assert.assertNotNull; 6 import static org.junit.Assert.assertSame; 7 import static org.junit.Assert.assertTrue; 8 9 import java.io.IOException; 10 import org.junit.Before; 11 import org.junit.Test; 12 import org.junit.runner.RunWith; 13 import org.robolectric.annotation.Implementation; 14 import org.robolectric.annotation.Implements; 15 import org.robolectric.annotation.RealObject; 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.ShadowWrangler; 20 import org.robolectric.shadow.api.Shadow; 21 import org.robolectric.testing.Foo; 22 import org.robolectric.testing.ShadowFoo; 23 24 @RunWith(SandboxTestRunner.class) 25 public class ShadowWranglerIntegrationTest { 26 private String name; 27 28 @Before 29 public void setUp() throws Exception { 30 name = "context"; 31 } 32 33 @Test 34 @SandboxConfig(shadows = {ShadowForAClassWithDefaultConstructor_HavingNoConstructorDelegate.class}) 35 public void testConstructorInvocation_WithDefaultConstructorAndNoConstructorDelegateOnShadowClass() throws Exception { 36 AClassWithDefaultConstructor instance = new AClassWithDefaultConstructor(); 37 assertThat(Shadow.<Object>extract(instance)).isExactlyInstanceOf(ShadowForAClassWithDefaultConstructor_HavingNoConstructorDelegate.class); 38 assertThat(instance.initialized).isTrue(); 39 } 40 41 @Test 42 @SandboxConfig(shadows = { ShadowFoo.class }) 43 public void testConstructorInvocation() throws Exception { 44 Foo foo = new Foo(name); 45 assertSame(name, shadowOf(foo).name); 46 } 47 48 @Test 49 @SandboxConfig(shadows = {ShadowFoo.class}) 50 public void testRealObjectAnnotatedFieldsAreSetBeforeConstructorIsCalled() throws Exception { 51 Foo foo = new Foo(name); 52 assertSame(name, shadowOf(foo).name); 53 assertSame(foo, shadowOf(foo).realFooField); 54 55 assertSame(foo, shadowOf(foo).realFooInConstructor); 56 assertSame(foo, shadowOf(foo).realFooInParentConstructor); 57 } 58 59 @Test 60 @SandboxConfig(shadows = {ShadowFoo.class}) 61 public void testMethodDelegation() throws Exception { 62 Foo foo = new Foo(name); 63 assertSame(name, foo.getName()); 64 } 65 66 @Test 67 @SandboxConfig(shadows = {WithEquals.class}) 68 public void testEqualsMethodDelegation() throws Exception { 69 Foo foo1 = new Foo(name); 70 Foo foo2 = new Foo(name); 71 assertEquals(foo1, foo2); 72 } 73 74 @Test 75 @SandboxConfig(shadows = {WithEquals.class}) 76 public void testHashCodeMethodDelegation() throws Exception { 77 Foo foo = new Foo(name); 78 assertEquals(42, foo.hashCode()); 79 } 80 81 @Test 82 @SandboxConfig(shadows = {WithToString.class}) 83 public void testToStringMethodDelegation() throws Exception { 84 Foo foo = new Foo(name); 85 assertEquals("the expected string", foo.toString()); 86 } 87 88 @Test 89 @SandboxConfig(shadows = {ShadowFoo.class}) 90 public void testShadowSelectionSearchesSuperclasses() throws Exception { 91 TextFoo textFoo = new TextFoo(name); 92 assertEquals(ShadowFoo.class, Shadow.extract(textFoo).getClass()); 93 } 94 95 @Test 96 @SandboxConfig(shadows = {ShadowFoo.class, ShadowTextFoo.class}) 97 public void shouldUseMostSpecificShadow() throws Exception { 98 TextFoo textFoo = new TextFoo(name); 99 assertThat(shadowOf(textFoo)).isInstanceOf(ShadowTextFoo.class); 100 } 101 102 @Test 103 public void testPrimitiveArrays() throws Exception { 104 Class<?> objArrayClass = ShadowWrangler.loadClass("java.lang.Object[]", getClass().getClassLoader()); 105 assertTrue(objArrayClass.isArray()); 106 assertEquals(Object.class, objArrayClass.getComponentType()); 107 108 Class<?> intArrayClass = ShadowWrangler.loadClass("int[]", getClass().getClassLoader()); 109 assertTrue(intArrayClass.isArray()); 110 assertEquals(Integer.TYPE, intArrayClass.getComponentType()); 111 } 112 113 @Test 114 @SandboxConfig(shadows = ShadowThrowInShadowMethod.class) 115 public void shouldRemoveNoiseFromShadowedStackTraces() throws Exception { 116 ThrowInShadowMethod instance = new ThrowInShadowMethod(); 117 118 Exception e = null; 119 try { 120 instance.method(); 121 } catch (Exception e1) { 122 e = e1; 123 } 124 125 assertNotNull(e); 126 assertEquals(IOException.class, e.getClass()); 127 assertEquals("fake exception", e.getMessage()); 128 StackTraceElement[] stackTrace = e.getStackTrace(); 129 130 assertThat(stackTrace[0].getClassName()).isEqualTo(ShadowThrowInShadowMethod.class.getName()); 131 assertThat(stackTrace[0].getMethodName()).isEqualTo("method"); 132 assertThat(stackTrace[0].getLineNumber()).isGreaterThan(0); 133 134 assertThat(stackTrace[1].getClassName()).isEqualTo(ThrowInShadowMethod.class.getName()); 135 assertThat(stackTrace[1].getMethodName()).isEqualTo("method"); 136 assertThat(stackTrace[1].getLineNumber()).isLessThan(0); 137 138 assertThat(stackTrace[2].getClassName()).isEqualTo(ShadowWranglerIntegrationTest.class.getName()); 139 assertThat(stackTrace[2].getMethodName()).isEqualTo("shouldRemoveNoiseFromShadowedStackTraces"); 140 assertThat(stackTrace[2].getLineNumber()).isGreaterThan(0); 141 } 142 143 @Instrument 144 public static class ThrowInShadowMethod { 145 public void method() throws IOException { 146 } 147 } 148 149 @Implements(ThrowInShadowMethod.class) 150 public static class ShadowThrowInShadowMethod { 151 public void method() throws IOException { 152 throw new IOException("fake exception"); 153 } 154 } 155 156 157 @Test 158 @SandboxConfig(shadows = ShadowThrowInRealMethod.class) 159 public void shouldRemoveNoiseFromUnshadowedStackTraces() throws Exception { 160 ThrowInRealMethod instance = new ThrowInRealMethod(); 161 162 Exception e = null; 163 try { 164 instance.method(); 165 } catch (Exception e1) { 166 e = e1; 167 } 168 169 assertNotNull(e); 170 assertEquals(IOException.class, e.getClass()); 171 assertEquals("fake exception", e.getMessage()); 172 StackTraceElement[] stackTrace = e.getStackTrace(); 173 174 assertThat(stackTrace[0].getClassName()).isEqualTo(ThrowInRealMethod.class.getName()); 175 assertThat(stackTrace[0].getMethodName()).isEqualTo("method"); 176 assertThat(stackTrace[0].getLineNumber()).isGreaterThan(0); 177 178 assertThat(stackTrace[1].getClassName()).isEqualTo(ShadowWranglerIntegrationTest.class.getName()); 179 assertThat(stackTrace[1].getMethodName()).isEqualTo("shouldRemoveNoiseFromUnshadowedStackTraces"); 180 assertThat(stackTrace[1].getLineNumber()).isGreaterThan(0); 181 } 182 183 @Instrument 184 public static class ThrowInRealMethod { 185 public void method() throws IOException { 186 throw new IOException("fake exception"); 187 } 188 } 189 190 @Implements(ThrowInRealMethod.class) 191 public static class ShadowThrowInRealMethod { 192 } 193 194 @Test @SandboxConfig(shadows = {ShadowOfChildWithInheritance.class, ShadowOfParent.class}) 195 public void whenInheritanceIsEnabled_shouldUseShadowSuperclassMethods() throws Exception { 196 assertThat(new Child().get()).isEqualTo("from shadow of parent"); 197 } 198 199 @Test @SandboxConfig(shadows = {ShadowOfChildWithoutInheritance.class, ShadowOfParent.class}) 200 public void whenInheritanceIsDisabled_shouldUseShadowSuperclassMethods() throws Exception { 201 assertThat(new Child().get()).isEqualTo("from child (from shadow of parent)"); 202 } 203 204 @Instrument 205 public static class Parent { 206 public String get() { 207 return "from parent"; 208 } 209 } 210 211 @Instrument 212 public static class Child extends Parent { 213 @Override 214 public String get() { 215 return "from child (" + super.get() + ")"; 216 } 217 } 218 219 @Implements(Parent.class) 220 public static class ShadowOfParent { 221 @Implementation 222 protected String get() { 223 return "from shadow of parent"; 224 } 225 } 226 227 @Implements(value = Child.class, inheritImplementationMethods = true) 228 public static class ShadowOfChildWithInheritance extends ShadowOfParent { 229 } 230 231 @Implements(value = Child.class, inheritImplementationMethods = false) 232 public static class ShadowOfChildWithoutInheritance extends ShadowOfParent { 233 } 234 235 private ShadowFoo shadowOf(Foo foo) { 236 return (ShadowFoo) Shadow.extract(foo); 237 } 238 239 private ShadowTextFoo shadowOf(TextFoo foo) { 240 return (ShadowTextFoo) Shadow.extract(foo); 241 } 242 243 @Implements(Foo.class) 244 public static class WithEquals { 245 @Implementation 246 protected void __constructor__(String s) { 247 } 248 249 @Override 250 public boolean equals(Object o) { 251 return true; 252 } 253 254 255 @Override 256 public int hashCode() { 257 return 42; 258 } 259 260 } 261 262 @Implements(Foo.class) 263 public static class WithToString { 264 @Implementation 265 protected void __constructor__(String s) { 266 } 267 268 @Override 269 public String toString() { 270 return "the expected string"; 271 } 272 } 273 274 @Implements(TextFoo.class) 275 public static class ShadowTextFoo extends ShadowFoo { 276 } 277 278 @Instrument 279 public static class TextFoo extends Foo { 280 public TextFoo(String s) { 281 super(s); 282 } 283 } 284 285 @Implements(Foo.class) 286 public static class ShadowFooParent { 287 @RealObject 288 private Foo realFoo; 289 public Foo realFooInParentConstructor; 290 291 @Implementation 292 protected void __constructor__(String name) { 293 realFooInParentConstructor = realFoo; 294 } 295 } 296 297 @Instrument 298 public static class AClassWithDefaultConstructor { 299 public boolean initialized; 300 301 public AClassWithDefaultConstructor() { 302 initialized = true; 303 } 304 } 305 306 @Implements(AClassWithDefaultConstructor.class) 307 public static class ShadowForAClassWithDefaultConstructor_HavingNoConstructorDelegate { 308 } 309 310 @SandboxConfig(shadows = ShadowAClassWithDifficultArgs.class) 311 @Test public void shouldAllowLooseSignatureMatches() throws Exception { 312 assertThat(new AClassWithDifficultArgs().aMethod("bc")).isEqualTo("abc"); 313 } 314 315 @Implements(value = AClassWithDifficultArgs.class, looseSignatures = true) 316 public static class ShadowAClassWithDifficultArgs { 317 @Implementation 318 protected Object aMethod(Object s) { 319 return "a" + s; 320 } 321 } 322 323 @Instrument 324 public static class AClassWithDifficultArgs { 325 public CharSequence aMethod(CharSequence s) { 326 return s; 327 } 328 } 329 } 330