1 package com.xtremelabs.robolectric.res; 2 3 import android.app.Activity; 4 import android.content.Context; 5 import android.os.Build; 6 import android.os.Bundle; 7 import android.view.View; 8 import android.view.ViewGroup; 9 import android.webkit.WebView; 10 import android.widget.*; 11 import com.google.android.maps.MapView; 12 import com.xtremelabs.robolectric.R; 13 import com.xtremelabs.robolectric.Robolectric; 14 import com.xtremelabs.robolectric.WithTestDefaultsRunner; 15 import com.xtremelabs.robolectric.shadows.ShadowImageView; 16 import com.xtremelabs.robolectric.shadows.ShadowTextView; 17 import com.xtremelabs.robolectric.util.CustomView; 18 import com.xtremelabs.robolectric.util.CustomView2; 19 import com.xtremelabs.robolectric.util.I18nException; 20 import com.xtremelabs.robolectric.util.TestUtil; 21 import org.junit.Before; 22 import org.junit.Test; 23 import org.junit.runner.RunWith; 24 25 import static com.xtremelabs.robolectric.Robolectric.shadowOf; 26 import static com.xtremelabs.robolectric.util.TestUtil.*; 27 import static org.hamcrest.CoreMatchers.*; 28 import static org.junit.Assert.*; 29 30 @RunWith(WithTestDefaultsRunner.class) 31 public class ViewLoaderTest { 32 private ViewLoader viewLoader; 33 private Context context; 34 35 @Before 36 public void setUp() throws Exception { 37 Robolectric.bindDefaultShadowClasses(); 38 39 ResourceExtractor resourceExtractor = new ResourceExtractor(); 40 resourceExtractor.addLocalRClass(R.class); 41 resourceExtractor.addSystemRClass(android.R.class); 42 43 StringResourceLoader stringResourceLoader = new StringResourceLoader(resourceExtractor); 44 new DocumentLoader(stringResourceLoader).loadResourceXmlDir(resourceFile("res", "values")); 45 new DocumentLoader(stringResourceLoader).loadSystemResourceXmlDir(getSystemResourceDir("values")); 46 47 viewLoader = new ViewLoader(resourceExtractor, new AttrResourceLoader(resourceExtractor)); 48 new DocumentLoader(viewLoader).loadResourceXmlDir(resourceFile("res", "layout")); 49 new DocumentLoader(viewLoader).loadResourceXmlDir(resourceFile("res", "layout-land")); 50 new DocumentLoader(viewLoader).loadResourceXmlDir(resourceFile("res", "layout-large-v16")); 51 new DocumentLoader(viewLoader).loadResourceXmlDir(resourceFile("res", "layout-v11")); 52 new DocumentLoader(viewLoader).loadResourceXmlDir(resourceFile("res", "layout-xlarge")); 53 new DocumentLoader(viewLoader).loadResourceXmlDir(resourceFile("res", "layout-xlarge-v11")); 54 new DocumentLoader(viewLoader).loadSystemResourceXmlDir(getSystemResourceDir("layout")); 55 56 context = new Activity(); 57 } 58 59 @Test 60 public void testCreatesCorrectClasses() throws Exception { 61 ViewGroup view = (ViewGroup) viewLoader.inflateView(context, "layout/media"); 62 TestUtil.assertInstanceOf(LinearLayout.class, view); 63 64 assertSame(context, view.getContext()); 65 } 66 67 @Test 68 public void testChoosesLayoutBasedOnDefaultScreenSize() throws Exception { 69 ViewGroup view = (ViewGroup) viewLoader.inflateView(context, "layout/different_screen_sizes"); 70 TextView textView = (TextView) view.findViewById(android.R.id.text1); 71 assertThat(textView.getText().toString(), equalTo("default")); 72 } 73 74 @Test 75 public void testChoosesLayoutBasedOnSearchPath_choosesFirstFileFoundOnPath() throws Exception { 76 viewLoader.setLayoutQualifierSearchPath("xlarge", "land"); 77 ViewGroup view = (ViewGroup) viewLoader.inflateView(context, "layout/different_screen_sizes"); 78 TextView textView = (TextView) view.findViewById(android.R.id.text1); 79 assertThat(textView.getText().toString(), equalTo("xlarge")); 80 } 81 82 @Test 83 public void testChoosesLayoutBasedOnSearchPath_respectsOrderOfPath() throws Exception { 84 viewLoader.setLayoutQualifierSearchPath("does-not-exist", "land", "xlarge"); 85 ViewGroup view = (ViewGroup) viewLoader.inflateView(context, "layout/different_screen_sizes"); 86 TextView textView = (TextView) view.findViewById(android.R.id.text1); 87 assertThat(textView.getText().toString(), equalTo("land")); 88 } 89 90 @Test 91 public void testChoosesLayoutBasedOnDefaultVersion() throws Exception { 92 Robolectric.Reflection.setFinalStaticField(Build.VERSION.class, "SDK_INT", Build.VERSION_CODES.FROYO); 93 ViewGroup view = (ViewGroup) viewLoader.inflateView(context, "layout/different_screen_sizes"); 94 TextView textView = (TextView) view.findViewById(android.R.id.text1); 95 assertThat(textView.getText().toString(), equalTo("default")); 96 } 97 98 @Test 99 public void testChoosesLayoutBasedOnNewestVersion() throws Exception { 100 Robolectric.Reflection.setFinalStaticField(Build.VERSION.class, "SDK_INT", Build.VERSION_CODES.JELLY_BEAN); 101 ViewGroup view = (ViewGroup) viewLoader.inflateView(context, "layout/different_screen_sizes"); 102 TextView textView = (TextView) view.findViewById(android.R.id.text1); 103 assertThat(textView.getText().toString(), equalTo("v11")); 104 } 105 106 @Test 107 public void testChoosesLayoutBasedOnSearchPath_choosesFirstFileFoundOnPathWithVersionNumber() throws Exception { 108 viewLoader.setLayoutQualifierSearchPath("xlarge", "large"); 109 Robolectric.Reflection.setFinalStaticField(Build.VERSION.class, "SDK_INT", Build.VERSION_CODES.HONEYCOMB); 110 ViewGroup view = (ViewGroup) viewLoader.inflateView(context, "layout/different_screen_sizes"); 111 TextView textView = (TextView) view.findViewById(android.R.id.text1); 112 assertThat(textView.getText().toString(), equalTo("xlarge-v11")); 113 } 114 115 @Test 116 public void testChoosesLayoutBasedOnSearchPath_choosesBestFileFoundOnPathWithVersionNumber() throws Exception { 117 viewLoader.setLayoutQualifierSearchPath("xlarge", "large"); 118 Robolectric.Reflection.setFinalStaticField(Build.VERSION.class, "SDK_INT", Build.VERSION_CODES.JELLY_BEAN); 119 ViewGroup view = (ViewGroup) viewLoader.inflateView(context, "layout/different_screen_sizes"); 120 TextView textView = (TextView) view.findViewById(android.R.id.text1); 121 assertThat(textView.getText().toString(), equalTo("xlarge-v11")); 122 } 123 124 @Test 125 public void testWebView() throws Exception { 126 ViewGroup view = (ViewGroup) viewLoader.inflateView(context, "layout/webview_holder"); 127 WebView webView = (WebView) view.findViewById(R.id.web_view); 128 129 webView.loadUrl("www.example.com"); 130 131 assertThat(shadowOf(webView).getLastLoadedUrl(), equalTo("www.example.com")); 132 } 133 134 @Test 135 public void testAddsChildren() throws Exception { 136 ViewGroup view = (ViewGroup) viewLoader.inflateView(context, "layout/media"); 137 assertTrue(view.getChildCount() > 0); 138 139 assertSame(context, view.getChildAt(0).getContext()); 140 } 141 142 @Test 143 public void testFindsChildrenById() throws Exception { 144 ViewGroup mediaView = (ViewGroup) viewLoader.inflateView(context, "layout/media"); 145 TestUtil.assertInstanceOf(TextView.class, mediaView.findViewById(R.id.title)); 146 147 ViewGroup mainView = (ViewGroup) viewLoader.inflateView(context, "layout/main"); 148 assertInstanceOf(View.class, mainView.findViewById(R.id.title)); 149 } 150 151 @Test 152 public void testInflatingConflictingSystemAndLocalViewsWorks() throws Exception { 153 ViewGroup view = (ViewGroup) viewLoader.inflateView(context, "layout/activity_list_item"); 154 assertInstanceOf(ImageView.class, view.findViewById(R.id.icon)); 155 156 view = (ViewGroup) viewLoader.inflateView(context, "android:layout/activity_list_item"); 157 assertInstanceOf(ImageView.class, view.findViewById(android.R.id.icon)); 158 } 159 160 @Test 161 public void testInclude() throws Exception { 162 ViewGroup mediaView = (ViewGroup) viewLoader.inflateView(context, "layout/media"); 163 assertInstanceOf(TextView.class, mediaView.findViewById(R.id.include_id)); 164 } 165 166 @Test 167 public void testIncludeShouldRetainAttributes() throws Exception { 168 ViewGroup mediaView = (ViewGroup) viewLoader.inflateView(context, "layout/media"); 169 assertThat(mediaView.findViewById(R.id.include_id).getVisibility(), is(View.GONE)); 170 } 171 172 @Test 173 public void shouldOverwriteIdOnIncludedNonMerge() throws Exception { 174 ViewGroup mediaView = (ViewGroup) viewLoader.inflateView(context, "layout/media"); 175 assertNull(mediaView.findViewById(R.id.snippet_text)); 176 } 177 178 @Test 179 public void shouldRetainIdOnIncludedMergeWhenIncludeSpecifiesNoId() throws Exception { 180 ViewGroup mediaView = (ViewGroup) viewLoader.inflateView(context, "layout/override_include"); 181 assertInstanceOf(TextView.class, mediaView.findViewById(R.id.inner_text)); 182 } 183 184 @Test 185 public void shouldRetainIdOnIncludedNonMergeWhenIncludeSpecifiesNoId() throws Exception { 186 ViewGroup mediaView = (ViewGroup) viewLoader.inflateView(context, "layout/override_include"); 187 assertInstanceOf(TextView.class, mediaView.findViewById(R.id.snippet_text)); 188 } 189 190 @Test 191 public void testIncludedIdShouldNotBeFoundWhenIncludedIsMerge() throws Exception { 192 ViewGroup overrideIncludeView = (ViewGroup) viewLoader.inflateView(context, "layout/outer"); 193 assertInstanceOf(LinearLayout.class, overrideIncludeView.findViewById(R.id.outer_merge)); 194 assertInstanceOf(TextView.class, overrideIncludeView.findViewById(R.id.inner_text)); 195 assertNull(overrideIncludeView.findViewById(R.id.include_id)); 196 assertEquals(1, overrideIncludeView.getChildCount()); 197 } 198 199 @Test 200 public void testIncludeShouldOverrideAttributesOfIncludedRootNode() throws Exception { 201 ViewGroup overrideIncludeView = (ViewGroup) viewLoader.inflateView(context, "layout/override_include"); 202 assertThat(overrideIncludeView.findViewById(R.id.snippet_text).getVisibility(), is(View.INVISIBLE)); 203 } 204 205 @Test 206 public void shouldNotCountRequestFocusElementAsChild() throws Exception { 207 ViewGroup viewGroup = (ViewGroup) viewLoader.inflateView(context, "layout/request_focus"); 208 ViewGroup frameLayout = (ViewGroup) viewGroup.getChildAt(1); 209 assertEquals(0, frameLayout.getChildCount()); 210 } 211 212 @Test 213 public void shouldGiveFocusToElementContainingRequestFocusElement() throws Exception { 214 ViewGroup viewGroup = (ViewGroup) viewLoader.inflateView(context, "layout/request_focus"); 215 EditText editText = (EditText) viewGroup.findViewById(R.id.edit_text); 216 assertFalse(editText.isFocused()); 217 } 218 219 @Test 220 public void shouldGiveFocusToFirstFocusableElement_butThisMightBeTheWrongBehavior() throws Exception { 221 ViewGroup viewGroup = (ViewGroup) viewLoader.inflateView(context, "layout/request_focus_with_two_edit_texts"); 222 assertTrue(viewGroup.findViewById(R.id.edit_text).isFocused()); 223 assertFalse(viewGroup.findViewById(R.id.edit_text2).isFocused()); 224 } 225 226 @Test 227 public void testMerge() throws Exception { 228 ViewGroup mediaView = (ViewGroup) viewLoader.inflateView(context, "layout/outer"); 229 TestUtil.assertInstanceOf(TextView.class, mediaView.findViewById(R.id.inner_text)); 230 } 231 232 @Test 233 public void mergeIncludesShouldNotCreateAncestryLoops() throws Exception { 234 ViewGroup mediaView = (ViewGroup) viewLoader.inflateView(context, "layout/outer"); 235 mediaView.hasFocus(); 236 } 237 238 @Test 239 public void testViewGroupsLooksAtItsOwnId() throws Exception { 240 TextView mediaView = (TextView) viewLoader.inflateView(context, "layout/snippet"); 241 assertSame(mediaView, mediaView.findViewById(R.id.snippet_text)); 242 } 243 244 @Test 245 public void shouldConstructCustomViewsWithAttributesConstructor() throws Exception { 246 CustomView view = (CustomView) viewLoader.inflateView(context, "layout/custom_layout"); 247 assertThat(view.attributeResourceValue, equalTo(R.string.hello)); 248 } 249 250 @Test 251 public void testViewVisibilityIsSet() throws Exception { 252 View mediaView = viewLoader.inflateView(context, "layout/media"); 253 assertThat(mediaView.findViewById(R.id.title).getVisibility(), equalTo(View.VISIBLE)); 254 assertThat(mediaView.findViewById(R.id.subtitle).getVisibility(), equalTo(View.GONE)); 255 } 256 257 @Test 258 public void testTextViewTextIsSet() throws Exception { 259 View mediaView = viewLoader.inflateView(context, "layout/main"); 260 assertThat(((TextView) mediaView.findViewById(R.id.title)).getText().toString(), equalTo("Main Layout")); 261 assertThat(((TextView) mediaView.findViewById(R.id.subtitle)).getText().toString(), equalTo("Hello")); 262 } 263 264 @Test 265 public void testTextViewCompoundDrawablesAreSet() throws Exception { 266 View mediaView = viewLoader.inflateView(context, "layout/main"); 267 ShadowTextView shadowTextView = shadowOf((TextView) mediaView.findViewById(R.id.title)); 268 269 assertThat(shadowTextView.getCompoundDrawablesImpl().getTop(), equalTo(R.drawable.an_image)); 270 assertThat(shadowTextView.getCompoundDrawablesImpl().getRight(), equalTo(R.drawable.an_other_image)); 271 assertThat(shadowTextView.getCompoundDrawablesImpl().getBottom(), equalTo(R.drawable.third_image)); 272 assertThat(shadowTextView.getCompoundDrawablesImpl().getLeft(), equalTo(R.drawable.fourth_image)); 273 } 274 275 @Test 276 public void testCheckBoxCheckedIsSet() throws Exception { 277 View mediaView = viewLoader.inflateView(context, "layout/main"); 278 assertThat(((CheckBox) mediaView.findViewById(R.id.true_checkbox)).isChecked(), equalTo(true)); 279 assertThat(((CheckBox) mediaView.findViewById(R.id.false_checkbox)).isChecked(), equalTo(false)); 280 assertThat(((CheckBox) mediaView.findViewById(R.id.default_checkbox)).isChecked(), equalTo(false)); 281 } 282 283 @Test 284 public void testImageViewSrcIsSet() throws Exception { 285 View mediaView = viewLoader.inflateView(context, "layout/main"); 286 assertThat(((ShadowImageView) shadowOf(mediaView.findViewById(R.id.image))).getResourceId(), equalTo(R.drawable.an_image)); 287 } 288 289 @Test 290 public void shouldInflateMergeLayoutIntoParent() throws Exception { 291 View innerMerge = viewLoader.inflateView(context, R.layout.inner_merge, new LinearLayout(null)); 292 assertNotNull(innerMerge); 293 } 294 295 @Test 296 public void testMapView() throws Exception { 297 RelativeLayout mainView = (RelativeLayout) viewLoader.inflateView(context, "layout/mapview"); 298 TestUtil.assertInstanceOf(MapView.class, mainView.findViewById(R.id.map_view)); 299 } 300 301 @Test 302 public void testViewEnabled() throws Exception { 303 View mediaView = viewLoader.inflateView(context, "layout/main"); 304 assertThat(mediaView.findViewById(R.id.time).isEnabled(), equalTo(false)); 305 } 306 307 @Test 308 public void testContentDescriptionIsSet() throws Exception { 309 View mediaView = viewLoader.inflateView(context, "layout/main"); 310 assertThat(mediaView.findViewById(R.id.time).getContentDescription().toString(), equalTo("Howdy")); 311 } 312 313 @Test 314 public void testViewBackgroundIdIsSet() throws Exception { 315 View mediaView = viewLoader.inflateView(context, "layout/main"); 316 ImageView imageView = (ImageView) mediaView.findViewById(R.id.image); 317 ShadowImageView shadowImageView = Robolectric.shadowOf(imageView); 318 319 assertThat(shadowImageView.getBackgroundResourceId(), equalTo(R.drawable.image_background)); 320 } 321 322 @Test 323 public void testOnClickAttribute() throws Exception { 324 ClickActivity activity = new ClickActivity(); 325 activity.onCreate(null); 326 327 assertThat(activity.clicked, equalTo(false)); 328 329 Button button = (Button)activity.findViewById(R.id.button); 330 button.performClick(); 331 332 assertThat(activity.clicked, equalTo(true)); 333 } 334 335 @Test 336 public void testInvalidOnClickAttribute() throws Exception { 337 Activity activity = new Activity(); 338 activity.setContentView(R.layout.with_invalid_onclick); 339 340 Button button = 341 (Button)activity.findViewById(R.id.invalid_onclick_button); 342 343 IllegalStateException exception = null; 344 try { 345 button.performClick(); 346 } catch (IllegalStateException e) { 347 exception = e; 348 } finally { 349 assertNotNull(exception); 350 assertThat("The error message should contain the id name of the " 351 + "faulty button", 352 exception.getMessage(), 353 containsString("invalid_onclick_button")); 354 } 355 } 356 357 @Test 358 public void shouldInvokeOnFinishInflate() throws Exception { 359 CustomView2 outerCustomView = (CustomView2) viewLoader.inflateView(context, "layout/custom_layout2"); 360 CustomView2 innerCustomView = (CustomView2) outerCustomView.getChildAt(0); 361 assertThat(outerCustomView.childCountAfterInflate, equalTo(1)); 362 assertThat(innerCustomView.childCountAfterInflate, equalTo(3)); 363 } 364 365 @Test 366 public void testIncludesLinearLayoutsOnlyOnce() throws Exception { 367 ViewGroup parentView = (ViewGroup) viewLoader.inflateView(context, "layout/included_layout_parent"); 368 assertEquals(1, parentView.getChildCount()); 369 } 370 371 @Test(expected=I18nException.class) 372 public void shouldThrowI18nExceptionOnLayoutWithBareStrings() throws Exception { 373 viewLoader.setStrictI18n(true); 374 new DocumentLoader(viewLoader).loadResourceXmlDir(resourceFile("res", "layout")); 375 new DocumentLoader(viewLoader).loadSystemResourceXmlDir(getSystemResourceDir("layout")); 376 377 viewLoader.inflateView(context,"layout/text_views"); 378 } 379 380 public static class ClickActivity extends Activity { 381 public boolean clicked = false; 382 383 @Override protected void onCreate(Bundle savedInstanceState) { 384 super.onCreate(savedInstanceState); 385 setContentView(R.layout.main); 386 } 387 388 public void onButtonClick(View v) { 389 clicked = true; 390 } 391 } 392 } 393