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