package com.xtremelabs.robolectric.res; import android.app.Activity; import android.content.Context; import android.os.Build; import android.os.Bundle; import android.view.View; import android.view.ViewGroup; import android.webkit.WebView; import android.widget.*; import com.google.android.maps.MapView; import com.xtremelabs.robolectric.R; import com.xtremelabs.robolectric.Robolectric; import com.xtremelabs.robolectric.WithTestDefaultsRunner; import com.xtremelabs.robolectric.shadows.ShadowImageView; import com.xtremelabs.robolectric.shadows.ShadowTextView; import com.xtremelabs.robolectric.util.CustomView; import com.xtremelabs.robolectric.util.CustomView2; import com.xtremelabs.robolectric.util.I18nException; import com.xtremelabs.robolectric.util.TestUtil; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import static com.xtremelabs.robolectric.Robolectric.shadowOf; import static com.xtremelabs.robolectric.util.TestUtil.*; import static org.hamcrest.CoreMatchers.*; import static org.junit.Assert.*; @RunWith(WithTestDefaultsRunner.class) public class ViewLoaderTest { private ViewLoader viewLoader; private Context context; @Before public void setUp() throws Exception { Robolectric.bindDefaultShadowClasses(); ResourceExtractor resourceExtractor = new ResourceExtractor(); resourceExtractor.addLocalRClass(R.class); resourceExtractor.addSystemRClass(android.R.class); StringResourceLoader stringResourceLoader = new StringResourceLoader(resourceExtractor); new DocumentLoader(stringResourceLoader).loadResourceXmlDir(resourceFile("res", "values")); new DocumentLoader(stringResourceLoader).loadSystemResourceXmlDir(getSystemResourceDir("values")); viewLoader = new ViewLoader(resourceExtractor, new AttrResourceLoader(resourceExtractor)); new DocumentLoader(viewLoader).loadResourceXmlDir(resourceFile("res", "layout")); new DocumentLoader(viewLoader).loadResourceXmlDir(resourceFile("res", "layout-land")); new DocumentLoader(viewLoader).loadResourceXmlDir(resourceFile("res", "layout-large-v16")); new DocumentLoader(viewLoader).loadResourceXmlDir(resourceFile("res", "layout-v11")); new DocumentLoader(viewLoader).loadResourceXmlDir(resourceFile("res", "layout-xlarge")); new DocumentLoader(viewLoader).loadResourceXmlDir(resourceFile("res", "layout-xlarge-v11")); new DocumentLoader(viewLoader).loadSystemResourceXmlDir(getSystemResourceDir("layout")); context = new Activity(); } @Test public void testCreatesCorrectClasses() throws Exception { ViewGroup view = (ViewGroup) viewLoader.inflateView(context, "layout/media"); TestUtil.assertInstanceOf(LinearLayout.class, view); assertSame(context, view.getContext()); } @Test public void testChoosesLayoutBasedOnDefaultScreenSize() throws Exception { ViewGroup view = (ViewGroup) viewLoader.inflateView(context, "layout/different_screen_sizes"); TextView textView = (TextView) view.findViewById(android.R.id.text1); assertThat(textView.getText().toString(), equalTo("default")); } @Test public void testChoosesLayoutBasedOnSearchPath_choosesFirstFileFoundOnPath() throws Exception { viewLoader.setLayoutQualifierSearchPath("xlarge", "land"); ViewGroup view = (ViewGroup) viewLoader.inflateView(context, "layout/different_screen_sizes"); TextView textView = (TextView) view.findViewById(android.R.id.text1); assertThat(textView.getText().toString(), equalTo("xlarge")); } @Test public void testChoosesLayoutBasedOnSearchPath_respectsOrderOfPath() throws Exception { viewLoader.setLayoutQualifierSearchPath("does-not-exist", "land", "xlarge"); ViewGroup view = (ViewGroup) viewLoader.inflateView(context, "layout/different_screen_sizes"); TextView textView = (TextView) view.findViewById(android.R.id.text1); assertThat(textView.getText().toString(), equalTo("land")); } @Test public void testChoosesLayoutBasedOnDefaultVersion() throws Exception { Robolectric.Reflection.setFinalStaticField(Build.VERSION.class, "SDK_INT", Build.VERSION_CODES.FROYO); ViewGroup view = (ViewGroup) viewLoader.inflateView(context, "layout/different_screen_sizes"); TextView textView = (TextView) view.findViewById(android.R.id.text1); assertThat(textView.getText().toString(), equalTo("default")); } @Test public void testChoosesLayoutBasedOnNewestVersion() throws Exception { Robolectric.Reflection.setFinalStaticField(Build.VERSION.class, "SDK_INT", Build.VERSION_CODES.JELLY_BEAN); ViewGroup view = (ViewGroup) viewLoader.inflateView(context, "layout/different_screen_sizes"); TextView textView = (TextView) view.findViewById(android.R.id.text1); assertThat(textView.getText().toString(), equalTo("v11")); } @Test public void testChoosesLayoutBasedOnSearchPath_choosesFirstFileFoundOnPathWithVersionNumber() throws Exception { viewLoader.setLayoutQualifierSearchPath("xlarge", "large"); Robolectric.Reflection.setFinalStaticField(Build.VERSION.class, "SDK_INT", Build.VERSION_CODES.HONEYCOMB); ViewGroup view = (ViewGroup) viewLoader.inflateView(context, "layout/different_screen_sizes"); TextView textView = (TextView) view.findViewById(android.R.id.text1); assertThat(textView.getText().toString(), equalTo("xlarge-v11")); } @Test public void testChoosesLayoutBasedOnSearchPath_choosesBestFileFoundOnPathWithVersionNumber() throws Exception { viewLoader.setLayoutQualifierSearchPath("xlarge", "large"); Robolectric.Reflection.setFinalStaticField(Build.VERSION.class, "SDK_INT", Build.VERSION_CODES.JELLY_BEAN); ViewGroup view = (ViewGroup) viewLoader.inflateView(context, "layout/different_screen_sizes"); TextView textView = (TextView) view.findViewById(android.R.id.text1); assertThat(textView.getText().toString(), equalTo("xlarge-v11")); } @Test public void testWebView() throws Exception { ViewGroup view = (ViewGroup) viewLoader.inflateView(context, "layout/webview_holder"); WebView webView = (WebView) view.findViewById(R.id.web_view); webView.loadUrl("www.example.com"); assertThat(shadowOf(webView).getLastLoadedUrl(), equalTo("www.example.com")); } @Test public void testAddsChildren() throws Exception { ViewGroup view = (ViewGroup) viewLoader.inflateView(context, "layout/media"); assertTrue(view.getChildCount() > 0); assertSame(context, view.getChildAt(0).getContext()); } @Test public void testFindsChildrenById() throws Exception { ViewGroup mediaView = (ViewGroup) viewLoader.inflateView(context, "layout/media"); TestUtil.assertInstanceOf(TextView.class, mediaView.findViewById(R.id.title)); ViewGroup mainView = (ViewGroup) viewLoader.inflateView(context, "layout/main"); assertInstanceOf(View.class, mainView.findViewById(R.id.title)); } @Test public void testInflatingConflictingSystemAndLocalViewsWorks() throws Exception { ViewGroup view = (ViewGroup) viewLoader.inflateView(context, "layout/activity_list_item"); assertInstanceOf(ImageView.class, view.findViewById(R.id.icon)); view = (ViewGroup) viewLoader.inflateView(context, "android:layout/activity_list_item"); assertInstanceOf(ImageView.class, view.findViewById(android.R.id.icon)); } @Test public void testInclude() throws Exception { ViewGroup mediaView = (ViewGroup) viewLoader.inflateView(context, "layout/media"); assertInstanceOf(TextView.class, mediaView.findViewById(R.id.include_id)); } @Test public void testIncludeShouldRetainAttributes() throws Exception { ViewGroup mediaView = (ViewGroup) viewLoader.inflateView(context, "layout/media"); assertThat(mediaView.findViewById(R.id.include_id).getVisibility(), is(View.GONE)); } @Test public void shouldOverwriteIdOnIncludedNonMerge() throws Exception { ViewGroup mediaView = (ViewGroup) viewLoader.inflateView(context, "layout/media"); assertNull(mediaView.findViewById(R.id.snippet_text)); } @Test public void shouldRetainIdOnIncludedMergeWhenIncludeSpecifiesNoId() throws Exception { ViewGroup mediaView = (ViewGroup) viewLoader.inflateView(context, "layout/override_include"); assertInstanceOf(TextView.class, mediaView.findViewById(R.id.inner_text)); } @Test public void shouldRetainIdOnIncludedNonMergeWhenIncludeSpecifiesNoId() throws Exception { ViewGroup mediaView = (ViewGroup) viewLoader.inflateView(context, "layout/override_include"); assertInstanceOf(TextView.class, mediaView.findViewById(R.id.snippet_text)); } @Test public void testIncludedIdShouldNotBeFoundWhenIncludedIsMerge() throws Exception { ViewGroup overrideIncludeView = (ViewGroup) viewLoader.inflateView(context, "layout/outer"); 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 { ViewGroup overrideIncludeView = (ViewGroup) viewLoader.inflateView(context, "layout/override_include"); assertThat(overrideIncludeView.findViewById(R.id.snippet_text).getVisibility(), is(View.INVISIBLE)); } @Test public void shouldNotCountRequestFocusElementAsChild() throws Exception { ViewGroup viewGroup = (ViewGroup) viewLoader.inflateView(context, "layout/request_focus"); ViewGroup frameLayout = (ViewGroup) viewGroup.getChildAt(1); assertEquals(0, frameLayout.getChildCount()); } @Test public void shouldGiveFocusToElementContainingRequestFocusElement() throws Exception { ViewGroup viewGroup = (ViewGroup) viewLoader.inflateView(context, "layout/request_focus"); EditText editText = (EditText) viewGroup.findViewById(R.id.edit_text); assertFalse(editText.isFocused()); } @Test public void shouldGiveFocusToFirstFocusableElement_butThisMightBeTheWrongBehavior() throws Exception { ViewGroup viewGroup = (ViewGroup) viewLoader.inflateView(context, "layout/request_focus_with_two_edit_texts"); assertTrue(viewGroup.findViewById(R.id.edit_text).isFocused()); assertFalse(viewGroup.findViewById(R.id.edit_text2).isFocused()); } @Test public void testMerge() throws Exception { ViewGroup mediaView = (ViewGroup) viewLoader.inflateView(context, "layout/outer"); TestUtil.assertInstanceOf(TextView.class, mediaView.findViewById(R.id.inner_text)); } @Test public void mergeIncludesShouldNotCreateAncestryLoops() throws Exception { ViewGroup mediaView = (ViewGroup) viewLoader.inflateView(context, "layout/outer"); mediaView.hasFocus(); } @Test public void testViewGroupsLooksAtItsOwnId() throws Exception { TextView mediaView = (TextView) viewLoader.inflateView(context, "layout/snippet"); assertSame(mediaView, mediaView.findViewById(R.id.snippet_text)); } @Test public void shouldConstructCustomViewsWithAttributesConstructor() throws Exception { CustomView view = (CustomView) viewLoader.inflateView(context, "layout/custom_layout"); assertThat(view.attributeResourceValue, equalTo(R.string.hello)); } @Test public void testViewVisibilityIsSet() throws Exception { View mediaView = viewLoader.inflateView(context, "layout/media"); assertThat(mediaView.findViewById(R.id.title).getVisibility(), equalTo(View.VISIBLE)); assertThat(mediaView.findViewById(R.id.subtitle).getVisibility(), equalTo(View.GONE)); } @Test public void testTextViewTextIsSet() throws Exception { View mediaView = viewLoader.inflateView(context, "layout/main"); assertThat(((TextView) mediaView.findViewById(R.id.title)).getText().toString(), equalTo("Main Layout")); assertThat(((TextView) mediaView.findViewById(R.id.subtitle)).getText().toString(), equalTo("Hello")); } @Test public void testTextViewCompoundDrawablesAreSet() throws Exception { View mediaView = viewLoader.inflateView(context, "layout/main"); ShadowTextView shadowTextView = shadowOf((TextView) mediaView.findViewById(R.id.title)); assertThat(shadowTextView.getCompoundDrawablesImpl().getTop(), equalTo(R.drawable.an_image)); assertThat(shadowTextView.getCompoundDrawablesImpl().getRight(), equalTo(R.drawable.an_other_image)); assertThat(shadowTextView.getCompoundDrawablesImpl().getBottom(), equalTo(R.drawable.third_image)); assertThat(shadowTextView.getCompoundDrawablesImpl().getLeft(), equalTo(R.drawable.fourth_image)); } @Test public void testCheckBoxCheckedIsSet() throws Exception { View mediaView = viewLoader.inflateView(context, "layout/main"); assertThat(((CheckBox) mediaView.findViewById(R.id.true_checkbox)).isChecked(), equalTo(true)); assertThat(((CheckBox) mediaView.findViewById(R.id.false_checkbox)).isChecked(), equalTo(false)); assertThat(((CheckBox) mediaView.findViewById(R.id.default_checkbox)).isChecked(), equalTo(false)); } @Test public void testImageViewSrcIsSet() throws Exception { View mediaView = viewLoader.inflateView(context, "layout/main"); assertThat(((ShadowImageView) shadowOf(mediaView.findViewById(R.id.image))).getResourceId(), equalTo(R.drawable.an_image)); } @Test public void shouldInflateMergeLayoutIntoParent() throws Exception { View innerMerge = viewLoader.inflateView(context, R.layout.inner_merge, new LinearLayout(null)); assertNotNull(innerMerge); } @Test public void testMapView() throws Exception { RelativeLayout mainView = (RelativeLayout) viewLoader.inflateView(context, "layout/mapview"); TestUtil.assertInstanceOf(MapView.class, mainView.findViewById(R.id.map_view)); } @Test public void testViewEnabled() throws Exception { View mediaView = viewLoader.inflateView(context, "layout/main"); assertThat(mediaView.findViewById(R.id.time).isEnabled(), equalTo(false)); } @Test public void testContentDescriptionIsSet() throws Exception { View mediaView = viewLoader.inflateView(context, "layout/main"); assertThat(mediaView.findViewById(R.id.time).getContentDescription().toString(), equalTo("Howdy")); } @Test public void testViewBackgroundIdIsSet() throws Exception { View mediaView = viewLoader.inflateView(context, "layout/main"); ImageView imageView = (ImageView) mediaView.findViewById(R.id.image); ShadowImageView shadowImageView = Robolectric.shadowOf(imageView); assertThat(shadowImageView.getBackgroundResourceId(), equalTo(R.drawable.image_background)); } @Test public void testOnClickAttribute() throws Exception { ClickActivity activity = new ClickActivity(); activity.onCreate(null); assertThat(activity.clicked, equalTo(false)); Button button = (Button)activity.findViewById(R.id.button); button.performClick(); assertThat(activity.clicked, equalTo(true)); } @Test public void testInvalidOnClickAttribute() throws Exception { Activity activity = new Activity(); 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; } finally { assertNotNull(exception); assertThat("The error message should contain the id name of the " + "faulty button", exception.getMessage(), containsString("invalid_onclick_button")); } } @Test public void shouldInvokeOnFinishInflate() throws Exception { CustomView2 outerCustomView = (CustomView2) viewLoader.inflateView(context, "layout/custom_layout2"); CustomView2 innerCustomView = (CustomView2) outerCustomView.getChildAt(0); assertThat(outerCustomView.childCountAfterInflate, equalTo(1)); assertThat(innerCustomView.childCountAfterInflate, equalTo(3)); } @Test public void testIncludesLinearLayoutsOnlyOnce() throws Exception { ViewGroup parentView = (ViewGroup) viewLoader.inflateView(context, "layout/included_layout_parent"); assertEquals(1, parentView.getChildCount()); } @Test(expected=I18nException.class) public void shouldThrowI18nExceptionOnLayoutWithBareStrings() throws Exception { viewLoader.setStrictI18n(true); new DocumentLoader(viewLoader).loadResourceXmlDir(resourceFile("res", "layout")); new DocumentLoader(viewLoader).loadSystemResourceXmlDir(getSystemResourceDir("layout")); viewLoader.inflateView(context,"layout/text_views"); } 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; } } }