package org.robolectric.shadows; import android.app.Activity; import android.content.Context; import android.content.pm.ActivityInfo; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.os.Bundle; import android.util.AttributeSet; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.view.ViewParent; import android.webkit.WebView; import android.widget.Button; import android.widget.CheckBox; import android.widget.EditText; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.ListView; import android.widget.RelativeLayout; import android.widget.TextView; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.robolectric.R; import org.robolectric.TestRunners; import org.robolectric.android.CustomStateView; import org.robolectric.android.CustomView; import org.robolectric.android.CustomView2; import org.robolectric.annotation.Config; import org.robolectric.util.ReflectionHelpers; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; import static org.robolectric.Robolectric.buildActivity; import static org.robolectric.Shadows.shadowOf; import static org.robolectric.test.Assertions.assertThat; import static org.robolectric.util.TestUtil.TEST_PACKAGE; import static org.robolectric.util.TestUtil.assertInstanceOf; @RunWith(TestRunners.MultiApiSelfTest.class) public class ShadowLayoutInflaterTest { private Activity context; @Before public void setUp() throws Exception { context = buildActivity(Activity.class).create().get(); } @Test public void testCreatesCorrectClasses() throws Exception { int layoutResId = context.getResources().getIdentifier("media", "layout", TEST_PACKAGE); ViewGroup view = (ViewGroup) LayoutInflater.from(context).inflate(layoutResId, null); assertInstanceOf(LinearLayout.class, view); assertSame(context, view.getContext()); } @Test public void testChoosesLayoutBasedOnDefaultScreenSize() throws Exception { int layoutResId = context.getResources().getIdentifier("different_screen_sizes", "layout", TEST_PACKAGE); ViewGroup view = (ViewGroup) LayoutInflater.from(context).inflate(layoutResId, null); TextView textView = (TextView) view.findViewById(android.R.id.text1); assertThat(textView.getText().toString()).isEqualTo("default"); } @Test @Config(qualifiers = "xlarge-land") public void testChoosesLayoutBasedOnSearchPath_choosesFirstFileFoundOnPath() throws Exception { int layoutResId = context.getResources().getIdentifier("different_screen_sizes", "layout", TEST_PACKAGE); ViewGroup view = (ViewGroup) LayoutInflater.from(context).inflate(layoutResId, null); TextView textView = (TextView) view.findViewById(android.R.id.text1); assertThat(textView.getText().toString()).isEqualTo("xlarge"); } @Test @Config(qualifiers = "doesnotexist-land-xlarge") public void testChoosesLayoutBasedOnSearchPath_respectsOrderOfPath() throws Exception { int layoutResId = context.getResources().getIdentifier("different_screen_sizes", "layout", TEST_PACKAGE); ViewGroup view = (ViewGroup) LayoutInflater.from(context).inflate(layoutResId, null); TextView textView = (TextView) view.findViewById(android.R.id.text1); assertThat(textView.getText().toString()).isEqualTo("land"); } @Test public void testWebView() throws Exception { int layoutResId = context.getResources().getIdentifier("webview_holder", "layout", TEST_PACKAGE); ViewGroup view = (ViewGroup) LayoutInflater.from(context).inflate(layoutResId, null); WebView webView = (WebView) view.findViewById(R.id.web_view); webView.loadUrl("www.example.com"); assertThat(shadowOf(webView).getLastLoadedUrl()).isEqualTo("www.example.com"); } @Test public void testAddsChildren() throws Exception { int layoutResId = context.getResources().getIdentifier("media", "layout", TEST_PACKAGE); ViewGroup view = (ViewGroup) LayoutInflater.from(context).inflate(layoutResId, null); assertTrue(view.getChildCount() > 0); assertSame(context, view.getChildAt(0).getContext()); } @Test public void testFindsChildrenById() throws Exception { int layoutResId1 = context.getResources().getIdentifier("media", "layout", TEST_PACKAGE); ViewGroup mediaView = (ViewGroup) LayoutInflater.from(context).inflate(layoutResId1, null); assertInstanceOf(TextView.class, mediaView.findViewById(R.id.title)); int layoutResId = context.getResources().getIdentifier("main", "layout", TEST_PACKAGE); ViewGroup mainView = (ViewGroup) LayoutInflater.from(context).inflate(layoutResId, null); assertInstanceOf(View.class, mainView.findViewById(R.id.title)); } @Test public void testInflatingConflictingSystemAndLocalViewsWorks() throws Exception { int layoutResId1 = context.getResources().getIdentifier("activity_list_item", "layout", TEST_PACKAGE); ViewGroup view = (ViewGroup) LayoutInflater.from(context).inflate(layoutResId1, null); assertInstanceOf(ImageView.class, view.findViewById(R.id.icon)); int layoutResId = context.getResources().getIdentifier("activity_list_item", "layout", "android"); view = (ViewGroup) LayoutInflater.from(context).inflate(layoutResId, null); assertInstanceOf(ImageView.class, view.findViewById(android.R.id.icon)); } @Test public void testInclude() throws Exception { int layoutResId = context.getResources().getIdentifier("media", "layout", TEST_PACKAGE); ViewGroup mediaView = (ViewGroup) LayoutInflater.from(context).inflate(layoutResId, null); assertInstanceOf(TextView.class, mediaView.findViewById(R.id.include_id)); } @Test public void testIncludeShouldRetainAttributes() throws Exception { int layoutResId = context.getResources().getIdentifier("media", "layout", TEST_PACKAGE); ViewGroup mediaView = (ViewGroup) LayoutInflater.from(context).inflate(layoutResId, null); assertThat(mediaView.findViewById(R.id.include_id).getVisibility()).isEqualTo(View.GONE); } @Test public void shouldOverwriteIdOnIncludedNonMerge() throws Exception { int layoutResId = context.getResources().getIdentifier("media", "layout", TEST_PACKAGE); ViewGroup mediaView = (ViewGroup) LayoutInflater.from(context).inflate(layoutResId, null); assertNull(mediaView.findViewById(R.id.snippet_text)); } @Test public void shouldRetainIdOnIncludedMergeWhenIncludeSpecifiesNoId() throws Exception { int layoutResId = context.getResources().getIdentifier("override_include", "layout", TEST_PACKAGE); ViewGroup mediaView = (ViewGroup) LayoutInflater.from(context).inflate(layoutResId, null); assertInstanceOf(TextView.class, mediaView.findViewById(R.id.inner_text)); } @Test public void shouldRetainIdOnIncludedNonMergeWhenIncludeSpecifiesNoId() throws Exception { int layoutResId = context.getResources().getIdentifier("override_include", "layout", TEST_PACKAGE); ViewGroup mediaView = (ViewGroup) LayoutInflater.from(context).inflate(layoutResId, null); assertInstanceOf(TextView.class, mediaView.findViewById(R.id.snippet_text)); } @Test public void testIncludedIdShouldNotBeFoundWhenIncludedIsMerge() throws Exception { int layoutResId = context.getResources().getIdentifier("outer", "layout", TEST_PACKAGE); ViewGroup overrideIncludeView = (ViewGroup) LayoutInflater.from(context).inflate(layoutResId, null); assertInstanceOf(LinearLayout.class, overrideIncludeView.findViewById(R.id.outer_merge)); assertInstanceOf(TextView.class, overrideIncludeView.findViewById(R.id.inner_text)); assertNull(overrideIncludeView.findViewById(R.id.include_id)); assertEquals(1, overrideIncludeView.getChildCount()); } @Test public void testIncludeShouldOverrideAttributesOfIncludedRootNode() throws Exception { int layoutResId = context.getResources().getIdentifier("override_include", "layout", TEST_PACKAGE); ViewGroup overrideIncludeView = (ViewGroup) LayoutInflater.from(context).inflate(layoutResId, null); assertThat(overrideIncludeView.findViewById(R.id.snippet_text).getVisibility()).isEqualTo(View.INVISIBLE); } @Test public void shouldNotCountRequestFocusElementAsChild() throws Exception { int layoutResId = context.getResources().getIdentifier("request_focus", "layout", TEST_PACKAGE); ViewGroup viewGroup = (ViewGroup) LayoutInflater.from(context).inflate(layoutResId, null); ViewGroup frameLayout = (ViewGroup) viewGroup.getChildAt(1); assertEquals(0, frameLayout.getChildCount()); } @Test public void focusRequest_shouldNotExplodeOnViewRootImpl() throws Exception { LinearLayout parent = new LinearLayout(context); shadowOf(parent).setMyParent(ReflectionHelpers.createNullProxy(ViewParent.class)); int layoutResId = context.getResources().getIdentifier("request_focus", "layout", TEST_PACKAGE); LayoutInflater.from(context).inflate(layoutResId, parent); } @Test public void shouldGiveFocusToElementContainingRequestFocusElement() throws Exception { int layoutResId = context.getResources().getIdentifier("request_focus", "layout", TEST_PACKAGE); ViewGroup viewGroup = (ViewGroup) LayoutInflater.from(context).inflate(layoutResId, null); EditText editText = (EditText) viewGroup.findViewById(R.id.edit_text); assertFalse(editText.isFocused()); } @Test public void testMerge() throws Exception { int layoutResId = context.getResources().getIdentifier("outer", "layout", TEST_PACKAGE); ViewGroup mediaView = (ViewGroup) LayoutInflater.from(context).inflate(layoutResId, null); assertInstanceOf(TextView.class, mediaView.findViewById(R.id.inner_text)); } @Test public void mergeIncludesShouldNotCreateAncestryLoops() throws Exception { int layoutResId = context.getResources().getIdentifier("outer", "layout", TEST_PACKAGE); ViewGroup mediaView = (ViewGroup) LayoutInflater.from(context).inflate(layoutResId, null); mediaView.hasFocus(); } @Test public void testViewGroupsLooksAtItsOwnId() throws Exception { int layoutResId = context.getResources().getIdentifier("snippet", "layout", TEST_PACKAGE); TextView mediaView = (TextView) LayoutInflater.from(context).inflate(layoutResId, null); assertSame(mediaView, mediaView.findViewById(R.id.snippet_text)); } @Test public void shouldConstructCustomViewsWithAttributesConstructor() throws Exception { int layoutResId = context.getResources().getIdentifier("custom_layout", "layout", TEST_PACKAGE); CustomView view = (CustomView) LayoutInflater.from(context).inflate(layoutResId, null); assertThat(view.attributeResourceValue).isEqualTo(R.string.hello); } @Test public void shouldConstructCustomViewsWithCustomState() throws Exception { int layoutResId = context.getResources().getIdentifier("custom_layout6", "layout", TEST_PACKAGE); CustomStateView view = (CustomStateView) LayoutInflater.from(context).inflate(layoutResId, null); assertThat(view.getDrawableState()).doesNotContain(R.attr.stateFoo); view.isFoo = true; view.refreshDrawableState(); assertThat(view.getDrawableState()).contains(R.attr.stateFoo); } @Test public void shouldConstructCustomViewsWithAttributesInResAutoNamespace() throws Exception { int layoutResId = context.getResources().getIdentifier("custom_layout5", "layout", TEST_PACKAGE); CustomView view = (CustomView) LayoutInflater.from(context).inflate(layoutResId, null); assertThat(view.attributeResourceValue).isEqualTo(R.string.hello); } @Test public void shouldConstructCustomViewsWithAttributesWithURLEncodedNamespaces() throws Exception { int layoutResId = context.getResources().getIdentifier("custom_layout4", "layout", TEST_PACKAGE); CustomView view = (CustomView) LayoutInflater.from(context).inflate(layoutResId, null) .findViewById(R.id.custom_view); assertThat(view.namespacedResourceValue).isEqualTo(R.layout.text_views); } @Test public void testViewVisibilityIsSet() throws Exception { int layoutResId = context.getResources().getIdentifier("media", "layout", TEST_PACKAGE); View mediaView = LayoutInflater.from(context).inflate(layoutResId, null); assertThat(mediaView.findViewById(R.id.title).getVisibility()).isEqualTo(View.VISIBLE); assertThat(mediaView.findViewById(R.id.subtitle).getVisibility()).isEqualTo(View.GONE); } @Test public void testTextViewTextIsSet() throws Exception { int layoutResId = context.getResources().getIdentifier("main", "layout", TEST_PACKAGE); View mediaView = LayoutInflater.from(context).inflate(layoutResId, null); assertThat(((TextView) mediaView.findViewById(R.id.title)).getText().toString()).isEqualTo("Main Layout"); assertThat(((TextView) mediaView.findViewById(R.id.subtitle)).getText().toString()).isEqualTo("Hello"); } @Test public void testTextViewCompoundDrawablesAreSet() throws Exception { int layoutResId = context.getResources().getIdentifier("main", "layout", TEST_PACKAGE); View mediaView = LayoutInflater.from(context).inflate(layoutResId, null); TextView view = (TextView) mediaView.findViewById(R.id.title); assertThat(view.getCompoundDrawables()[0]).isEqualTo(drawable(R.drawable.fourth_image)); assertThat(view.getCompoundDrawables()[1]).isEqualTo(drawable(R.drawable.an_image)); assertThat(view.getCompoundDrawables()[2]).isEqualTo(drawable(R.drawable.an_other_image)); assertThat(view.getCompoundDrawables()[3]).isEqualTo(drawable(R.drawable.third_image)); } @Test public void testCheckBoxCheckedIsSet() throws Exception { int layoutResId = context.getResources().getIdentifier("main", "layout", TEST_PACKAGE); View mediaView = LayoutInflater.from(context).inflate(layoutResId, null); assertThat(((CheckBox) mediaView.findViewById(R.id.true_checkbox)).isChecked()).isTrue(); assertThat(((CheckBox) mediaView.findViewById(R.id.false_checkbox)).isChecked()).isFalse(); assertThat(((CheckBox) mediaView.findViewById(R.id.default_checkbox)).isChecked()).isFalse(); } @Test public void testImageViewSrcIsSet() throws Exception { int layoutResId = context.getResources().getIdentifier("main", "layout", TEST_PACKAGE); View mediaView = LayoutInflater.from(context).inflate(layoutResId, null); ImageView imageView = (ImageView) mediaView.findViewById(R.id.image); BitmapDrawable drawable = (BitmapDrawable) imageView.getDrawable(); assertThat(shadowOf(drawable.getBitmap()).getCreatedFromResId()).isEqualTo(R.drawable.an_image); } @Test public void testImageViewSrcIsSetFromMipmap() throws Exception { int layoutResId = context.getResources().getIdentifier("main", "layout", TEST_PACKAGE); View mediaView = LayoutInflater.from(context).inflate(layoutResId, null); ImageView imageView = (ImageView) mediaView.findViewById(R.id.mipmapImage); BitmapDrawable drawable = (BitmapDrawable) imageView.getDrawable(); assertThat(shadowOf(drawable.getBitmap()).getCreatedFromResId()).isEqualTo(R.mipmap.robolectric); } @Test public void shouldInflateMergeLayoutIntoParent() throws Exception { LinearLayout linearLayout = new LinearLayout(context); int layoutResId = context.getResources().getIdentifier("inner_merge", "layout", TEST_PACKAGE); View innerMerge = LayoutInflater.from(context).inflate(layoutResId, linearLayout); assertThat(linearLayout.getChildAt(0)).isInstanceOf(TextView.class); } @Test public void testMultiOrientation() throws Exception { context = buildActivity(Activity.class).create().start().resume().get(); // Default screen orientation should be portrait. int layoutResId1 = context.getResources().getIdentifier("multi_orientation", "layout", TEST_PACKAGE); ViewGroup view = (ViewGroup) LayoutInflater.from(context).inflate(layoutResId1, null); assertInstanceOf(LinearLayout.class, view); assertEquals(view.getId(), R.id.portrait); assertSame(context, view.getContext()); // Confirm explicit "orientation = portrait" works. context.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); int layoutResId = context.getResources().getIdentifier("multi_orientation", "layout", TEST_PACKAGE); view = (ViewGroup) LayoutInflater.from(context).inflate(layoutResId, null); assertInstanceOf(LinearLayout.class, view); assertEquals(view.getId(), R.id.portrait); assertSame(context, view.getContext()); } @Test @Config(qualifiers = "land") public void testMultiOrientation_explicitLandscape() throws Exception { context = buildActivity(Activity.class).create().start().resume().get(); // Confirm explicit "orientation = landscape" works. context.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); int layoutResId = context.getResources().getIdentifier("multi_orientation", "layout", TEST_PACKAGE); ViewGroup view = (ViewGroup) LayoutInflater.from(context).inflate(layoutResId, null); assertEquals(view.getId(), R.id.landscape); assertInstanceOf(LinearLayout.class, view); } @Test @Config(qualifiers = "w0dp") public void testSetContentViewByItemResource() throws Exception { Activity activity = buildActivity(Activity.class).create().get(); activity.setContentView(R.layout.main_layout); TextView tv1 = (TextView) activity.findViewById(R.id.hello); TextView tv2 = (TextView) activity.findViewById(R.id.world); assertNotNull(tv1); assertNull(tv2); } @Test @Config(qualifiers = "w820dp") public void testSetContentViewByItemResourceWithW820dp() throws Exception { Activity activity = buildActivity(Activity.class).create().get(); activity.setContentView(R.layout.main_layout); TextView tv1 = (TextView) activity.findViewById(R.id.hello); TextView tv2 = (TextView) activity.findViewById(R.id.world); assertNotNull(tv1); assertNotNull(tv2); } @Test public void testViewEnabled() throws Exception { int layoutResId = context.getResources().getIdentifier("main", "layout", TEST_PACKAGE); View mediaView = LayoutInflater.from(context).inflate(layoutResId, null); assertThat(mediaView.findViewById(R.id.time).isEnabled()).isFalse(); } @Test public void testContentDescriptionIsSet() throws Exception { int layoutResId = context.getResources().getIdentifier("main", "layout", TEST_PACKAGE); View mediaView = LayoutInflater.from(context).inflate(layoutResId, null); assertThat(mediaView.findViewById(R.id.time).getContentDescription().toString()).isEqualTo("Howdy"); } @Test public void testAlphaIsSet() throws Exception { int layoutResId = context.getResources().getIdentifier("main", "layout", TEST_PACKAGE); View mediaView = LayoutInflater.from(context).inflate(layoutResId, null); assertThat(mediaView.findViewById(R.id.time).getAlpha()).isEqualTo(.3f); } @Test public void testViewBackgroundIdIsSet() throws Exception { int layoutResId = context.getResources().getIdentifier("main", "layout", TEST_PACKAGE); View mediaView = LayoutInflater.from(context).inflate(layoutResId, null); ImageView imageView = (ImageView) mediaView.findViewById(R.id.image); assertThat(imageView.getBackground()).isResource(R.drawable.image_background); assertThat(shadowOf(imageView).getBackgroundResourceId()).isEqualTo(R.drawable.image_background); } @Test public void testOnClickAttribute() throws Exception { ClickActivity activity = buildActivity(ClickActivity.class).create().get(); assertThat(activity.clicked).isFalse(); Button button = (Button)activity.findViewById(R.id.button); button.performClick(); assertThat(activity.clicked).isTrue(); } @Test public void testInvalidOnClickAttribute() throws Exception { Activity activity = buildActivity(Activity.class).create().get(); activity.setContentView(R.layout.with_invalid_onclick); Button button = (Button)activity.findViewById(R.id.invalid_onclick_button); IllegalStateException exception = null; try { button.performClick(); } catch (IllegalStateException e) { exception = e; } assertNotNull(exception); assertThat(exception.getMessage()) .as("The error message should contain the id name of the faulty button") .contains("invalid_onclick_button"); } @Test public void shouldInvokeOnFinishInflate() throws Exception { int layoutResId = context.getResources().getIdentifier("custom_layout2", "layout", TEST_PACKAGE); CustomView2 outerCustomView = (CustomView2) LayoutInflater.from(context).inflate(layoutResId, null); CustomView2 innerCustomView = (CustomView2) outerCustomView.getChildAt(0); assertThat(outerCustomView.childCountAfterInflate).isEqualTo(1); assertThat(innerCustomView.childCountAfterInflate).isEqualTo(3); } @SuppressWarnings("UnusedDeclaration") public static class CustomView3 extends TextView { public CustomView3(Context context) { super(context); } public CustomView3(Context context, AttributeSet attrs) { super(context, attrs); } public CustomView3(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } } @Test public void shouldInflateViewsWithClassAttr() throws Exception { int layoutResId = context.getResources().getIdentifier("custom_layout3", "layout", TEST_PACKAGE); CustomView3 outerCustomView = (CustomView3) LayoutInflater.from(context).inflate(layoutResId, null); assertThat(outerCustomView.getText().toString()).isEqualTo("Hello bonjour"); } @Test public void testIncludesLinearLayoutsOnlyOnce() throws Exception { int layoutResId = context.getResources().getIdentifier("included_layout_parent", "layout", TEST_PACKAGE); ViewGroup parentView = (ViewGroup) LayoutInflater.from(context).inflate(layoutResId, null); assertEquals(1, parentView.getChildCount()); } @Test public void testConverterAcceptsEnumOrdinal() throws Exception { int layoutResId = context.getResources().getIdentifier("ordinal_scrollbar", "layout", TEST_PACKAGE); ViewGroup view = (ViewGroup) LayoutInflater.from(context).inflate(layoutResId, null); assertInstanceOf(RelativeLayout.class, view); ListView listView = (ListView) view.findViewById(org.robolectric.R.id.list_view_with_enum_scrollbar); assertInstanceOf(ListView.class, listView); } ///////////////////////// private Drawable drawable(int id) { Drawable drawable = context.getResources().getDrawable(id); drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight()); return drawable; } public static class ClickActivity extends Activity { public boolean clicked = false; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); } public void onButtonClick(View v) { clicked = true; } } }