Home | History | Annotate | Download | only in shadows
      1 package org.robolectric.shadows;
      2 
      3 import static org.assertj.core.api.Assertions.assertThat;
      4 import static org.junit.Assert.assertEquals;
      5 import static org.junit.Assert.assertFalse;
      6 import static org.junit.Assert.assertNotNull;
      7 import static org.junit.Assert.assertNull;
      8 import static org.junit.Assert.assertSame;
      9 import static org.junit.Assert.assertTrue;
     10 import static org.robolectric.Robolectric.buildActivity;
     11 import static org.robolectric.Shadows.shadowOf;
     12 
     13 import android.app.Activity;
     14 import android.content.Context;
     15 import android.content.pm.ActivityInfo;
     16 import android.graphics.drawable.BitmapDrawable;
     17 import android.graphics.drawable.Drawable;
     18 import android.os.Bundle;
     19 import android.util.AttributeSet;
     20 import android.view.LayoutInflater;
     21 import android.view.View;
     22 import android.view.ViewGroup;
     23 import android.view.ViewParent;
     24 import android.webkit.WebView;
     25 import android.widget.Button;
     26 import android.widget.CheckBox;
     27 import android.widget.EditText;
     28 import android.widget.ImageView;
     29 import android.widget.LinearLayout;
     30 import android.widget.ListView;
     31 import android.widget.RelativeLayout;
     32 import android.widget.TextView;
     33 import org.junit.Before;
     34 import org.junit.Test;
     35 import org.junit.runner.RunWith;
     36 import org.robolectric.R;
     37 import org.robolectric.R.layout;
     38 import org.robolectric.RobolectricTestRunner;
     39 import org.robolectric.RuntimeEnvironment;
     40 import org.robolectric.android.CustomStateView;
     41 import org.robolectric.android.CustomView;
     42 import org.robolectric.android.CustomView2;
     43 import org.robolectric.annotation.Config;
     44 import org.robolectric.util.ReflectionHelpers;
     45 
     46 @RunWith(RobolectricTestRunner.class)
     47 public class ShadowLayoutInflaterTest {
     48   private Context context;
     49 
     50   @Before
     51   public void setUp() throws Exception {
     52     context = RuntimeEnvironment.application;
     53   }
     54 
     55   @Test
     56   public void testCreatesCorrectClasses() throws Exception {
     57     ViewGroup view = inflate(R.layout.media);
     58     assertThat(view).isInstanceOf((Class<? extends ViewGroup>) LinearLayout.class);
     59 
     60     assertSame(context, view.getContext());
     61   }
     62 
     63   @Test
     64   public void testChoosesLayoutBasedOnDefaultScreenSize() throws Exception {
     65     ViewGroup view = inflate(R.layout.different_screen_sizes);
     66     TextView textView = view.findViewById(android.R.id.text1);
     67     assertThat(textView.getText().toString()).isEqualTo("default");
     68   }
     69 
     70   @Test
     71   @Config(qualifiers = "xlarge")
     72   public void testChoosesLayoutBasedOnScreenSize() throws Exception {
     73     ViewGroup view = inflate(R.layout.different_screen_sizes);
     74     TextView textView = view.findViewById(android.R.id.text1);
     75     assertThat(textView.getText().toString()).isEqualTo("xlarge");
     76   }
     77 
     78   @Test
     79   @Config(qualifiers = "land")
     80   public void testChoosesLayoutBasedOnQualifiers() throws Exception {
     81     ViewGroup view = inflate(R.layout.different_screen_sizes);
     82     TextView textView = view.findViewById(android.R.id.text1);
     83     assertThat(textView.getText().toString()).isEqualTo("land");
     84   }
     85 
     86   @Test
     87   public void testWebView() throws Exception {
     88     ViewGroup view = inflate(R.layout.webview_holder);
     89     WebView webView = view.findViewById(R.id.web_view);
     90 
     91     webView.loadUrl("www.example.com");
     92 
     93     assertThat(shadowOf(webView).getLastLoadedUrl()).isEqualTo("www.example.com");
     94   }
     95 
     96   @Test
     97   public void testAddsChildren() throws Exception {
     98     ViewGroup view = inflate(R.layout.media);
     99     assertTrue(view.getChildCount() > 0);
    100 
    101     assertSame(context, view.getChildAt(0).getContext());
    102   }
    103 
    104   @Test
    105   public void testFindsChildrenById() throws Exception {
    106     ViewGroup mediaView = inflate(R.layout.media);
    107     assertThat(mediaView.<TextView>findViewById(R.id.title))
    108         .isInstanceOf((Class<? extends TextView>) TextView.class);
    109 
    110     ViewGroup mainView = inflate(R.layout.main);
    111     assertThat(mainView.<View>findViewById(R.id.title))
    112         .isInstanceOf((Class<? extends View>) View.class);
    113   }
    114 
    115   @Test
    116   public void testInflatingConflictingSystemAndLocalViewsWorks() throws Exception {
    117     ViewGroup view = inflate(R.layout.activity_list_item);
    118     assertThat(view.<ImageView>findViewById(R.id.icon))
    119         .isInstanceOf((Class<? extends ImageView>) ImageView.class);
    120 
    121     view = inflate(android.R.layout.activity_list_item);
    122     assertThat(view.<ImageView>findViewById(android.R.id.icon))
    123         .isInstanceOf((Class<? extends ImageView>) ImageView.class);
    124   }
    125 
    126   @Test
    127   public void testInclude() throws Exception {
    128     ViewGroup mediaView = inflate(R.layout.media);
    129     assertThat(mediaView.<TextView>findViewById(R.id.include_id))
    130         .isInstanceOf((Class<? extends TextView>) TextView.class);
    131   }
    132 
    133   @Test
    134   public void testIncludeShouldRetainAttributes() throws Exception {
    135     ViewGroup mediaView = inflate(R.layout.media);
    136     assertThat(mediaView.findViewById(R.id.include_id).getVisibility()).isEqualTo(View.GONE);
    137   }
    138 
    139   @Test
    140   public void shouldOverwriteIdOnIncludedNonMerge() throws Exception {
    141     ViewGroup mediaView = inflate(R.layout.media);
    142     assertNull(mediaView.findViewById(R.id.snippet_text));
    143   }
    144 
    145   @Test
    146   public void shouldRetainIdOnIncludedMergeWhenIncludeSpecifiesNoId() throws Exception {
    147     ViewGroup mediaView = inflate(R.layout.override_include);
    148     assertThat(mediaView.<TextView>findViewById(R.id.inner_text))
    149         .isInstanceOf((Class<? extends TextView>) TextView.class);
    150   }
    151 
    152   @Test
    153   public void shouldRetainIdOnIncludedNonMergeWhenIncludeSpecifiesNoId() throws Exception {
    154     ViewGroup mediaView = inflate(R.layout.override_include);
    155     assertThat(mediaView.<TextView>findViewById(R.id.snippet_text))
    156         .isInstanceOf((Class<? extends TextView>) TextView.class);
    157   }
    158 
    159   @Test
    160   public void testIncludedIdShouldNotBeFoundWhenIncludedIsMerge() throws Exception {
    161     ViewGroup overrideIncludeView = inflate(R.layout.outer);
    162     assertThat(overrideIncludeView.<LinearLayout>findViewById(R.id.outer_merge))
    163         .isInstanceOf((Class<? extends LinearLayout>) LinearLayout.class);
    164     assertThat(overrideIncludeView.<TextView>findViewById(R.id.inner_text))
    165         .isInstanceOf((Class<? extends TextView>) TextView.class);
    166     assertNull(overrideIncludeView.findViewById(R.id.include_id));
    167     assertEquals(1, overrideIncludeView.getChildCount());
    168   }
    169 
    170   @Test
    171   public void testIncludeShouldOverrideAttributesOfIncludedRootNode() throws Exception {
    172     ViewGroup overrideIncludeView = inflate(R.layout.override_include);
    173     assertThat(overrideIncludeView.findViewById(R.id.snippet_text).getVisibility())
    174         .isEqualTo(View.INVISIBLE);
    175   }
    176 
    177   @Test
    178   public void shouldNotCountRequestFocusElementAsChild() throws Exception {
    179     ViewGroup viewGroup = inflate(R.layout.request_focus);
    180     ViewGroup frameLayout = (ViewGroup) viewGroup.getChildAt(1);
    181     assertEquals(0, frameLayout.getChildCount());
    182   }
    183 
    184   @Test
    185   public void focusRequest_shouldNotExplodeOnViewRootImpl() throws Exception {
    186     LinearLayout parent = new LinearLayout(context);
    187     shadowOf(parent).setMyParent(ReflectionHelpers.createNullProxy(ViewParent.class));
    188     LayoutInflater.from(context).inflate(R.layout.request_focus, parent);
    189   }
    190 
    191   @Test
    192   public void shouldGiveFocusToElementContainingRequestFocusElement() throws Exception {
    193     ViewGroup viewGroup = inflate(R.layout.request_focus);
    194     EditText editText = viewGroup.findViewById(R.id.edit_text);
    195     assertFalse(editText.isFocused());
    196   }
    197 
    198   @Test
    199   public void testMerge() throws Exception {
    200     ViewGroup mediaView = inflate(R.layout.outer);
    201     assertThat(mediaView.<TextView>findViewById(R.id.inner_text))
    202         .isInstanceOf((Class<? extends TextView>) TextView.class);
    203   }
    204 
    205   @Test
    206   public void mergeIncludesShouldNotCreateAncestryLoops() throws Exception {
    207     ViewGroup mediaView = inflate(R.layout.outer);
    208     mediaView.hasFocus();
    209   }
    210 
    211   @Test
    212   public void testViewGroupsLooksAtItsOwnId() throws Exception {
    213     TextView mediaView = inflate(layout.snippet);
    214     assertSame(mediaView, mediaView.findViewById(R.id.snippet_text));
    215   }
    216 
    217   @Test
    218   public void shouldConstructCustomViewsWithAttributesConstructor() throws Exception {
    219     CustomView view = inflate(layout.custom_layout);
    220     assertThat(view.attributeResourceValue).isEqualTo(R.string.hello);
    221   }
    222 
    223   @Test
    224   public void shouldConstructCustomViewsWithCustomState() throws Exception {
    225     CustomStateView view = inflate(layout.custom_layout6);
    226     assertThat(view.getDrawableState()).doesNotContain(R.attr.stateFoo);
    227 
    228     view.isFoo = true;
    229     view.refreshDrawableState();
    230 
    231     assertThat(view.getDrawableState()).contains(R.attr.stateFoo);
    232   }
    233 
    234   @Test
    235   public void shouldConstructCustomViewsWithAttributesInResAutoNamespace() throws Exception {
    236     CustomView view = inflate(layout.custom_layout5);
    237     assertThat(view.attributeResourceValue).isEqualTo(R.string.hello);
    238   }
    239 
    240   @Test
    241   public void shouldConstructCustomViewsWithAttributesWithURLEncodedNamespaces() throws Exception {
    242     CustomView view = inflate(layout.custom_layout4).findViewById(R.id.custom_view);
    243     assertThat(view.namespacedResourceValue).isEqualTo(R.layout.text_views);
    244   }
    245 
    246   @Test
    247   public void testViewVisibilityIsSet() throws Exception {
    248     View mediaView = inflate(layout.media);
    249     assertThat(mediaView.findViewById(R.id.title).getVisibility()).isEqualTo(View.VISIBLE);
    250     assertThat(mediaView.findViewById(R.id.subtitle).getVisibility()).isEqualTo(View.GONE);
    251   }
    252 
    253   @Test
    254   public void testTextViewTextIsSet() throws Exception {
    255     View mediaView = inflate(layout.main);
    256     assertThat(((TextView) mediaView.findViewById(R.id.title)).getText().toString())
    257         .isEqualTo("Main Layout");
    258     assertThat(((TextView) mediaView.findViewById(R.id.subtitle)).getText().toString())
    259         .isEqualTo("Hello");
    260   }
    261 
    262   @Test
    263   public void testTextViewCompoundDrawablesAreSet() throws Exception {
    264     View mediaView = inflate(layout.main);
    265     TextView view = mediaView.findViewById(R.id.title);
    266 
    267     assertThat(view.getCompoundDrawables()[0]).isEqualTo(drawable(R.drawable.fourth_image));
    268     assertThat(view.getCompoundDrawables()[1]).isEqualTo(drawable(R.drawable.an_image));
    269     assertThat(view.getCompoundDrawables()[2]).isEqualTo(drawable(R.drawable.an_other_image));
    270     assertThat(view.getCompoundDrawables()[3]).isEqualTo(drawable(R.drawable.third_image));
    271   }
    272 
    273   @Test
    274   public void testCheckBoxCheckedIsSet() throws Exception {
    275     View mediaView = inflate(layout.main);
    276     assertThat(((CheckBox) mediaView.findViewById(R.id.true_checkbox)).isChecked()).isTrue();
    277     assertThat(((CheckBox) mediaView.findViewById(R.id.false_checkbox)).isChecked()).isFalse();
    278     assertThat(((CheckBox) mediaView.findViewById(R.id.default_checkbox)).isChecked()).isFalse();
    279   }
    280 
    281   @Test
    282   public void testImageViewSrcIsSet() throws Exception {
    283     View mediaView = inflate(layout.main);
    284     ImageView imageView = mediaView.findViewById(R.id.image);
    285     BitmapDrawable drawable = (BitmapDrawable) imageView.getDrawable();
    286     assertThat(shadowOf(drawable.getBitmap()).getCreatedFromResId()).isEqualTo(R.drawable.an_image);
    287   }
    288 
    289   @Test
    290   public void testImageViewSrcIsSetFromMipmap() throws Exception {
    291     View mediaView = inflate(layout.main);
    292     ImageView imageView = mediaView.findViewById(R.id.mipmapImage);
    293     BitmapDrawable drawable = (BitmapDrawable) imageView.getDrawable();
    294     assertThat(shadowOf(drawable.getBitmap()).getCreatedFromResId())
    295         .isEqualTo(R.mipmap.robolectric);
    296   }
    297 
    298   @Test
    299   public void shouldInflateMergeLayoutIntoParent() throws Exception {
    300     LinearLayout linearLayout = new LinearLayout(context);
    301     LayoutInflater.from(context).inflate(R.layout.inner_merge, linearLayout);
    302     assertThat(linearLayout.getChildAt(0)).isInstanceOf(TextView.class);
    303   }
    304 
    305   @Test
    306   public void testMultiOrientation() throws Exception {
    307     Activity activity = buildActivity(Activity.class).create().start().resume().get();
    308 
    309     // Default screen orientation should be portrait.
    310     ViewGroup view =
    311         (ViewGroup) LayoutInflater.from(activity).inflate(layout.multi_orientation, null);
    312     assertThat(view).isInstanceOf((Class<? extends ViewGroup>) LinearLayout.class);
    313     assertThat(view.getId()).isEqualTo(R.id.portrait);
    314     assertSame(activity, view.getContext());
    315 
    316     // Confirm explicit "orientation = portrait" works.
    317     activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
    318     int layoutResId = R.layout.multi_orientation;
    319     view = (ViewGroup) LayoutInflater.from(activity).inflate(layoutResId, null);
    320     assertThat(view).isInstanceOf((Class<? extends ViewGroup>) LinearLayout.class);
    321     assertThat(view.getId()).isEqualTo(R.id.portrait);
    322     assertSame(activity, view.getContext());
    323   }
    324 
    325   @Test
    326   @Config(qualifiers = "land")
    327   public void testMultiOrientation_explicitLandscape() throws Exception {
    328     Activity activity = buildActivity(Activity.class).create().start().resume().get();
    329 
    330     // Confirm explicit "orientation = landscape" works.
    331     activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
    332     ViewGroup view =
    333         (ViewGroup) LayoutInflater.from(activity).inflate(layout.multi_orientation, null);
    334     assertThat(view.getId()).isEqualTo(R.id.landscape);
    335     assertThat(view).isInstanceOf((Class<? extends ViewGroup>) LinearLayout.class);
    336   }
    337 
    338   @Test
    339   @Config(qualifiers = "w0dp")
    340   public void testSetContentViewByItemResource() throws Exception {
    341     Activity activity = buildActivity(Activity.class).create().get();
    342     activity.setContentView(R.layout.main_layout);
    343 
    344     TextView tv1 = activity.findViewById(R.id.hello);
    345     TextView tv2 = activity.findViewById(R.id.world);
    346     assertNotNull(tv1);
    347     assertNull(tv2);
    348   }
    349 
    350   @Test
    351   @Config(qualifiers = "w820dp")
    352   public void testSetContentViewByItemResourceWithW820dp() throws Exception {
    353     Activity activity = buildActivity(Activity.class).create().get();
    354     activity.setContentView(R.layout.main_layout);
    355 
    356     TextView tv1 = activity.findViewById(R.id.hello);
    357     TextView tv2 = activity.findViewById(R.id.world);
    358     assertNotNull(tv1);
    359     assertNotNull(tv2);
    360   }
    361 
    362   @Test
    363   public void testViewEnabled() throws Exception {
    364     View mediaView = inflate(layout.main);
    365     assertThat(mediaView.findViewById(R.id.time).isEnabled()).isFalse();
    366   }
    367 
    368   @Test
    369   public void testContentDescriptionIsSet() throws Exception {
    370     View mediaView = inflate(layout.main);
    371     assertThat(mediaView.findViewById(R.id.time).getContentDescription().toString())
    372         .isEqualTo("Howdy");
    373   }
    374 
    375   @Test
    376   public void testAlphaIsSet() throws Exception {
    377     View mediaView = inflate(layout.main);
    378     assertThat(mediaView.findViewById(R.id.time).getAlpha()).isEqualTo(.3f);
    379   }
    380 
    381   @Test
    382   public void testViewBackgroundIdIsSet() throws Exception {
    383     View mediaView = inflate(layout.main);
    384     ImageView imageView = mediaView.findViewById(R.id.image);
    385 
    386     assertThat(shadowOf(imageView.getBackground()).getCreatedFromResId())
    387         .isEqualTo(R.drawable.image_background);
    388   }
    389 
    390   @Test
    391   public void testOnClickAttribute() throws Exception {
    392     ClickActivity activity = buildActivity(ClickActivity.class).create().get();
    393 
    394     assertThat(activity.clicked).isFalse();
    395 
    396     Button button = activity.findViewById(R.id.button);
    397     button.performClick();
    398 
    399     assertThat(activity.clicked).isTrue();
    400   }
    401 
    402   @Test
    403   public void testInvalidOnClickAttribute() throws Exception {
    404     Activity activity = buildActivity(Activity.class).create().get();
    405     activity.setContentView(R.layout.with_invalid_onclick);
    406 
    407     Button button = activity.findViewById(R.id.invalid_onclick_button);
    408 
    409     IllegalStateException exception = null;
    410     try {
    411       button.performClick();
    412     } catch (IllegalStateException e) {
    413       exception = e;
    414     }
    415     assertNotNull(exception);
    416     assertThat(exception.getMessage())
    417         .as("The error message should contain the id name of the faulty button")
    418         .contains("invalid_onclick_button");
    419   }
    420 
    421   @Test
    422   public void shouldInvokeOnFinishInflate() throws Exception {
    423     int layoutResId = R.layout.custom_layout2;
    424     CustomView2 outerCustomView = inflate(layoutResId);
    425     CustomView2 innerCustomView = (CustomView2) outerCustomView.getChildAt(0);
    426     assertThat(outerCustomView.childCountAfterInflate).isEqualTo(1);
    427     assertThat(innerCustomView.childCountAfterInflate).isEqualTo(3);
    428   }
    429 
    430   @SuppressWarnings("UnusedDeclaration")
    431   public static class CustomView3 extends TextView {
    432     public CustomView3(Context context) {
    433       super(context);
    434     }
    435 
    436     public CustomView3(Context context, AttributeSet attrs) {
    437       super(context, attrs);
    438     }
    439 
    440     public CustomView3(Context context, AttributeSet attrs, int defStyle) {
    441       super(context, attrs, defStyle);
    442     }
    443   }
    444 
    445   @Test
    446   public void shouldInflateViewsWithClassAttr() throws Exception {
    447     CustomView3 outerCustomView = inflate(layout.custom_layout3);
    448     assertThat(outerCustomView.getText().toString()).isEqualTo("Hello bonjour");
    449   }
    450 
    451   @Test
    452   public void testIncludesLinearLayoutsOnlyOnce() throws Exception {
    453     ViewGroup parentView = inflate(R.layout.included_layout_parent);
    454     assertEquals(1, parentView.getChildCount());
    455   }
    456 
    457   @Test
    458   public void testConverterAcceptsEnumOrdinal() throws Exception {
    459     ViewGroup view = inflate(R.layout.ordinal_scrollbar);
    460     assertThat(view).isInstanceOf((Class<? extends ViewGroup>) RelativeLayout.class);
    461     ListView listView =
    462         (ListView) view.findViewById(org.robolectric.R.id.list_view_with_enum_scrollbar);
    463     assertThat(listView).isInstanceOf((Class<? extends ListView>) ListView.class);
    464   }
    465 
    466   /////////////////////////
    467 
    468   @SuppressWarnings("TypeParameterUnusedInFormals")
    469   private <T extends View> T inflate(int layoutResId) {
    470     return (T) LayoutInflater.from(context).inflate(layoutResId, null);
    471   }
    472 
    473   private Drawable drawable(int id) {
    474     Drawable drawable = context.getResources().getDrawable(id);
    475     drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
    476     return drawable;
    477   }
    478 
    479   public static class ClickActivity extends Activity {
    480     public boolean clicked = false;
    481 
    482     @Override
    483     protected void onCreate(Bundle savedInstanceState) {
    484       super.onCreate(savedInstanceState);
    485       setContentView(R.layout.main);
    486     }
    487 
    488     public void onButtonClick(View v) {
    489       clicked = true;
    490     }
    491   }
    492 }
    493