Home | History | Annotate | Download | only in robolectric
      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