1 package com.xtremelabs.robolectric.shadows; 2 3 import android.app.Activity; 4 import android.content.Context; 5 import android.graphics.Point; 6 import android.graphics.Rect; 7 import android.graphics.drawable.ColorDrawable; 8 import android.view.*; 9 import android.view.View.MeasureSpec; 10 import android.view.animation.Animation; 11 import android.widget.LinearLayout; 12 import com.xtremelabs.robolectric.R; 13 import com.xtremelabs.robolectric.Robolectric; 14 import com.xtremelabs.robolectric.WithTestDefaultsRunner; 15 import com.xtremelabs.robolectric.util.*; 16 import org.junit.Before; 17 import org.junit.Test; 18 import org.junit.runner.RunWith; 19 20 import static com.xtremelabs.robolectric.Robolectric.shadowOf; 21 import static junit.framework.Assert.assertEquals; 22 import static org.hamcrest.CoreMatchers.*; 23 import static org.hamcrest.MatcherAssert.assertThat; 24 import static org.junit.Assert.*; 25 26 @RunWith(WithTestDefaultsRunner.class) 27 public class ViewTest { 28 private View view; 29 30 @Before 31 public void setUp() throws Exception { 32 view = new View(new Activity()); 33 } 34 35 @Test 36 public void testHasEmptyLayoutParams() throws Exception { 37 ViewGroup.LayoutParams layoutParams = view.getLayoutParams(); 38 assertThat(layoutParams, notNullValue()); 39 } 40 41 @Test 42 public void layout_shouldAffectWidthAndHeight() throws Exception { 43 assertThat(view.getWidth(), equalTo(0)); 44 assertThat(view.getHeight(), equalTo(0)); 45 46 view.layout(100, 200, 303, 404); 47 assertThat(view.getWidth(), equalTo(303 - 100)); 48 assertThat(view.getHeight(), equalTo(404 - 200)); 49 } 50 51 @Test 52 public void shouldFocus() throws Exception { 53 final Transcript transcript = new Transcript(); 54 55 view.setOnFocusChangeListener(new View.OnFocusChangeListener() { 56 @Override 57 public void onFocusChange(View v, boolean hasFocus) { 58 transcript.add(hasFocus ? "Gained focus" : "Lost focus"); 59 } 60 }); 61 62 assertFalse(view.isFocused()); 63 assertFalse(view.hasFocus()); 64 transcript.assertNoEventsSoFar(); 65 66 view.requestFocus(); 67 assertTrue(view.isFocused()); 68 assertTrue(view.hasFocus()); 69 transcript.assertEventsSoFar("Gained focus"); 70 71 view.clearFocus(); 72 assertFalse(view.isFocused()); 73 assertFalse(view.hasFocus()); 74 transcript.assertEventsSoFar("Lost focus"); 75 } 76 77 @Test 78 public void shouldNotBeFocusableByDefault() throws Exception { 79 assertFalse(view.isFocusable()); 80 81 view.setFocusable(true); 82 assertTrue(view.isFocusable()); 83 } 84 85 @Test 86 public void shouldKnowIfThisOrAncestorsAreVisible() throws Exception { 87 assertTrue(shadowOf(view).derivedIsVisible()); 88 89 ViewGroup grandParent = new LinearLayout(null); 90 ViewGroup parent = new LinearLayout(null); 91 grandParent.addView(parent); 92 parent.addView(view); 93 94 grandParent.setVisibility(View.GONE); 95 96 assertFalse(shadowOf(view).derivedIsVisible()); 97 } 98 99 @Test 100 public void shouldInflateMergeRootedLayoutAndNotCreateReferentialLoops() throws Exception { 101 LinearLayout root = new LinearLayout(null); 102 root.inflate(new Activity(), R.layout.inner_merge, root); 103 for (int i = 0; i < root.getChildCount(); i++) { 104 View child = root.getChildAt(i); 105 assertNotSame(root, child); 106 } 107 } 108 109 @Test 110 public void performLongClick_shouldClickOnView() throws Exception { 111 TestOnLongClickListener clickListener = new TestOnLongClickListener(); 112 view.setOnLongClickListener(clickListener); 113 shadowOf(view).performLongClick(); 114 115 assertTrue(clickListener.clicked); 116 } 117 118 @Test 119 public void checkedClick_shouldClickOnView() throws Exception { 120 TestOnClickListener clickListener = new TestOnClickListener(); 121 view.setOnClickListener(clickListener); 122 shadowOf(view).checkedPerformClick(); 123 124 assertTrue(clickListener.clicked); 125 } 126 127 @Test(expected = RuntimeException.class) 128 public void checkedClick_shouldThrowIfViewIsNotVisible() throws Exception { 129 ViewGroup grandParent = new LinearLayout(null); 130 ViewGroup parent = new LinearLayout(null); 131 grandParent.addView(parent); 132 parent.addView(view); 133 grandParent.setVisibility(View.GONE); 134 135 shadowOf(view).checkedPerformClick(); 136 } 137 138 @Test(expected = RuntimeException.class) 139 public void checkedClick_shouldThrowIfViewIsDisabled() throws Exception { 140 view.setEnabled(false); 141 shadowOf(view).checkedPerformClick(); 142 } 143 144 @Test 145 public void getBackground_shouldReturnNullIfNoBackgroundHasBeenSet() throws Exception { 146 assertThat(view.getBackground(), nullValue()); 147 } 148 149 @Test 150 public void shouldSetBackgroundColor() { 151 view.setBackgroundColor(R.color.android_red); 152 int intColor = view.getResources().getColor(R.color.android_red); 153 154 assertThat((ColorDrawable) view.getBackground(), equalTo(new ColorDrawable(intColor))); 155 } 156 157 @Test 158 public void shouldSetBackgroundResource() throws Exception { 159 view.setBackgroundResource(R.drawable.an_image); 160 assertThat(view.getBackground(), equalTo(view.getResources().getDrawable(R.drawable.an_image))); 161 } 162 163 @Test 164 public void shouldRecordBackgroundColor() { 165 int[] colors = {0, 1, 727}; 166 167 for (int color : colors) { 168 view.setBackgroundColor(color); 169 assertThat(shadowOf(view).getBackgroundColor(), equalTo(color)); 170 } 171 } 172 173 @Test 174 public void shouldPostActionsToTheMessageQueue() throws Exception { 175 Robolectric.pauseMainLooper(); 176 177 TestRunnable runnable = new TestRunnable(); 178 view.post(runnable); 179 assertFalse(runnable.wasRun); 180 181 Robolectric.unPauseMainLooper(); 182 assertTrue(runnable.wasRun); 183 } 184 185 @Test 186 public void shouldPostInvalidateDelayed() throws Exception { 187 Robolectric.pauseMainLooper(); 188 189 view.postInvalidateDelayed(100); 190 ShadowView shadowView = shadowOf(view); 191 assertFalse(shadowView.wasInvalidated()); 192 193 Robolectric.unPauseMainLooper(); 194 assertTrue(shadowView.wasInvalidated()); 195 } 196 197 @Test 198 public void shouldPostActionsToTheMessageQueueWithDelay() throws Exception { 199 Robolectric.pauseMainLooper(); 200 201 TestRunnable runnable = new TestRunnable(); 202 view.postDelayed(runnable, 1); 203 assertFalse(runnable.wasRun); 204 205 Robolectric.getUiThreadScheduler().advanceBy(1); 206 assertTrue(runnable.wasRun); 207 } 208 209 @Test 210 public void shouldSupportAllConstructors() throws Exception { 211 new View(null); 212 new View(null, null); 213 new View(null, null, 0); 214 } 215 216 @Test 217 public void shouldSetAnimation() throws Exception { 218 Animation anim = new TestAnimation(); 219 view.setAnimation(anim); 220 assertThat(view.getAnimation(), sameInstance(anim)); 221 } 222 223 @Test 224 public void shouldStartAndClearAnimation() throws Exception { 225 Animation anim = new TestAnimation(); 226 TestAnimationListener listener = new TestAnimationListener(); 227 anim.setAnimationListener(listener); 228 assertThat(listener.wasStartCalled, equalTo(false)); 229 assertThat(listener.wasRepeatCalled, equalTo(false)); 230 assertThat(listener.wasEndCalled, equalTo(false)); 231 view.startAnimation(anim); 232 assertThat(listener.wasStartCalled, equalTo(true)); 233 assertThat(listener.wasRepeatCalled, equalTo(false)); 234 assertThat(listener.wasEndCalled, equalTo(false)); 235 view.clearAnimation(); 236 assertThat(listener.wasStartCalled, equalTo(true)); 237 assertThat(listener.wasRepeatCalled, equalTo(false)); 238 assertThat(listener.wasEndCalled, equalTo(true)); 239 } 240 241 @Test 242 public void shouldFindViewWithTag() { 243 view.setTag("tagged"); 244 assertThat(view.findViewWithTag("tagged"), sameInstance(view)); 245 } 246 247 @Test 248 public void shouldFindViewWithTag_whenViewOverridesGetTag() throws Exception { 249 View view = new View(Robolectric.application) { 250 @Override 251 public Object getTag() { 252 return "blarg"; 253 } 254 }; 255 assertThat(view.findViewWithTag("blarg"), sameInstance(view)); 256 } 257 258 @Test 259 public void scrollTo_shouldStoreTheScrolledCoordinates() throws Exception { 260 view.scrollTo(1, 2); 261 assertThat(shadowOf(view).scrollToCoordinates, equalTo(new Point(1, 2))); 262 } 263 264 @Test 265 public void shouldScrollTo() throws Exception { 266 view.scrollTo(7, 6); 267 268 assertEquals(7, view.getScrollX()); 269 assertEquals(6, view.getScrollY()); 270 } 271 272 @Test 273 public void shouldGetScrollXAndY() { 274 assertEquals(0, view.getScrollX()); 275 assertEquals(0, view.getScrollY()); 276 } 277 278 @Test 279 public void getViewTreeObserver_shouldReturnTheSameObserverFromMultipleCalls() throws Exception { 280 ViewTreeObserver observer = view.getViewTreeObserver(); 281 assertThat(observer, instanceOf(ViewTreeObserver.class)); 282 assertThat(view.getViewTreeObserver(), sameInstance(observer)); 283 } 284 285 @Test 286 public void dispatchTouchEvent_sendsMotionEventToOnTouchEvent() throws Exception { 287 TouchableView touchableView = new TouchableView(null); 288 MotionEvent event = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 12f, 34f, 0); 289 touchableView.dispatchTouchEvent(event); 290 assertThat(touchableView.event, sameInstance(event)); 291 view.dispatchTouchEvent(event); 292 assertThat(shadowOf(view).getLastTouchEvent(), sameInstance(event)); 293 } 294 295 @Test 296 public void dispatchTouchEvent_listensToTrueFromListener() throws Exception { 297 view.setOnTouchListener(new View.OnTouchListener() { 298 @Override 299 public boolean onTouch(View view, MotionEvent motionEvent) { 300 return true; 301 } 302 }); 303 MotionEvent event = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 12f, 34f, 0); 304 view.dispatchTouchEvent(event); 305 assertThat(shadowOf(view).getLastTouchEvent(), nullValue()); 306 } 307 308 @Test 309 public void dispatchTouchEvent_listensToFalseFromListener() throws Exception { 310 view.setOnTouchListener(new View.OnTouchListener() { 311 @Override 312 public boolean onTouch(View view, MotionEvent motionEvent) { 313 return false; 314 } 315 }); 316 MotionEvent event = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 12f, 34f, 0); 317 view.dispatchTouchEvent(event); 318 assertThat(shadowOf(view).getLastTouchEvent(), sameInstance(event)); 319 } 320 321 @Test 322 public void dispatchFocusEvent_sendsFocusToOnFocusChanged() throws Exception { 323 FocusableView focusableView = new FocusableView(null); 324 focusableView.requestFocus(); 325 assertThat(focusableView.onFocusChangedWasCalled, equalTo(true)); 326 } 327 328 @Test 329 public void test_nextFocusDownId() throws Exception { 330 assertEquals(View.NO_ID, view.getNextFocusDownId()); 331 332 view.setNextFocusDownId(R.id.icon); 333 assertEquals(R.id.icon, view.getNextFocusDownId()); 334 } 335 336 @Test 337 public void dispatchOnAnimationEnd() throws Exception { 338 TestView view1 = new TestView(new Activity()); 339 assertFalse(view1.onAnimationEndWasCalled); 340 shadowOf(view1).finishedAnimation(); 341 assertTrue(view1.onAnimationEndWasCalled); 342 } 343 344 @Test 345 public void test_measuredDimension() { 346 // View does not provide its own onMeasure implementation 347 TestView view1 = new TestView(new Activity()); 348 349 assertThat(view1.getHeight(), equalTo(0)); 350 assertThat(view1.getWidth(), equalTo(0)); 351 assertThat(view1.getMeasuredHeight(), equalTo(0)); 352 assertThat(view1.getMeasuredWidth(), equalTo(0)); 353 354 view1.measure( MeasureSpec.makeMeasureSpec(150, MeasureSpec.AT_MOST), 355 MeasureSpec.makeMeasureSpec(300, MeasureSpec.AT_MOST) ); 356 357 assertThat(view1.getHeight(), equalTo(0)); 358 assertThat(view1.getWidth(), equalTo(0)); 359 assertThat(view1.getMeasuredHeight(), equalTo(300)); 360 assertThat(view1.getMeasuredWidth(), equalTo(150)); 361 } 362 363 @Test 364 public void test_measuredDimensionCustomView() { 365 // View provides its own onMeasure implementation 366 TestView2 view2 = new TestView2(new Activity()); 367 368 assertThat(view2.getHeight(), equalTo(0)); 369 assertThat(view2.getWidth(), equalTo(0)); 370 assertThat(view2.getMeasuredHeight(), equalTo(0)); 371 assertThat(view2.getMeasuredWidth(), equalTo(0)); 372 373 view2.measure( MeasureSpec.makeMeasureSpec(1000, MeasureSpec.AT_MOST), 374 MeasureSpec.makeMeasureSpec(600, MeasureSpec.AT_MOST) ); 375 376 assertThat(view2.getHeight(), equalTo(0)); 377 assertThat(view2.getWidth(), equalTo(0)); 378 assertThat(view2.getMeasuredHeight(), equalTo(400)); 379 assertThat(view2.getMeasuredWidth(), equalTo(800)); 380 } 381 382 @Test 383 public void testFilterTouchesWhenObscured() { 384 assertFalse(view.getFilterTouchesWhenObscured()); 385 386 view.setFilterTouchesWhenObscured(true); 387 assertTrue(view.getFilterTouchesWhenObscured()); 388 389 view.setFilterTouchesWhenObscured(false); 390 assertFalse(view.getFilterTouchesWhenObscured()); 391 } 392 393 @Test 394 public void testFilterTouchesWhenObscuredWhenLoadedFromXml() { 395 LinearLayout root = new LinearLayout(null); 396 ShadowView.inflate(new Activity(), R.layout.views, root); 397 398 View defaultView = root.findViewById(R.id.default_view); 399 assertFalse(defaultView.getFilterTouchesWhenObscured()); 400 401 View filterFalseView = root.findViewById(R.id.filter_touches_false_view); 402 assertFalse(filterFalseView.getFilterTouchesWhenObscured()); 403 404 View filterTrueView = root.findViewById(R.id.filter_touches_true_view); 405 assertTrue(filterTrueView.getFilterTouchesWhenObscured()); 406 } 407 408 @Test 409 public void testClickable() { 410 assertFalse(view.isClickable()); 411 412 view.setClickable(true); 413 assertTrue(view.isClickable()); 414 415 view.setClickable(false); 416 assertFalse(view.isClickable()); 417 } 418 419 @Test 420 public void testClickableWhenLoadedFromXml() { 421 LinearLayout root = new LinearLayout(null); 422 ShadowView.inflate(new Activity(), R.layout.views, root); 423 424 View defaultView = root.findViewById(R.id.default_view); 425 assertFalse(defaultView.isClickable()); 426 427 View clickableFalseView = root.findViewById(R.id.clickable_false_view); 428 assertFalse(clickableFalseView.isClickable()); 429 430 View clickableTrueView = root.findViewById(R.id.clickable_true_view); 431 assertTrue(clickableTrueView.isClickable()); 432 } 433 434 @Test 435 public void testFocusable() { 436 assertFalse(view.isFocusable()); 437 438 view.setFocusable(true); 439 assertTrue(view.isFocusable()); 440 441 view.setFocusable(false); 442 assertFalse(view.isFocusable()); 443 } 444 445 @Test 446 public void testFocusableWhenLoadedFromXml() { 447 LinearLayout root = new LinearLayout(null); 448 ShadowView.inflate(new Activity(), R.layout.views, root); 449 450 View defaultView = root.findViewById(R.id.default_view); 451 assertFalse(defaultView.isFocusable()); 452 453 View focusableFalseView = root.findViewById(R.id.focusable_false_view); 454 assertFalse(focusableFalseView.isFocusable()); 455 456 View focusableTrueView = root.findViewById(R.id.focusable_true_view); 457 assertTrue(focusableTrueView.isFocusable()); 458 } 459 460 private static class TestAnimation extends Animation { 461 } 462 463 private static class TouchableView extends View { 464 MotionEvent event; 465 466 public TouchableView(Context context) { 467 super(context); 468 } 469 470 @Override 471 public boolean onTouchEvent(MotionEvent event) { 472 this.event = event; 473 return false; 474 } 475 } 476 477 private static class TestView extends View { 478 boolean onAnimationEndWasCalled; 479 480 public TestView(Context context) { 481 super(context); 482 } 483 484 @Override 485 protected void onAnimationEnd() { 486 super.onAnimationEnd(); 487 onAnimationEndWasCalled = true; 488 } 489 } 490 491 private static class FocusableView extends View { 492 boolean onFocusChangedWasCalled; 493 494 public FocusableView(Context context) { 495 super(context); 496 } 497 498 @Override 499 protected void onFocusChanged(boolean gainFocus, int direction, Rect previouslyFocusedRect) { 500 super.onFocusChanged(gainFocus, direction, previouslyFocusedRect); 501 502 onFocusChangedWasCalled = true; 503 } 504 } 505 506 private static class TestView2 extends View { 507 public TestView2(Context context) { 508 super(context); 509 } 510 511 @Override 512 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 513 super.onMeasure(800, 400); 514 } 515 } 516 } 517