package com.xtremelabs.robolectric.shadows; import android.app.Activity; import android.content.Context; import android.graphics.Point; import android.graphics.Rect; import android.graphics.drawable.ColorDrawable; import android.view.*; import android.view.View.MeasureSpec; import android.view.animation.Animation; import android.widget.LinearLayout; import com.xtremelabs.robolectric.R; import com.xtremelabs.robolectric.Robolectric; import com.xtremelabs.robolectric.WithTestDefaultsRunner; import com.xtremelabs.robolectric.util.*; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import static com.xtremelabs.robolectric.Robolectric.shadowOf; import static junit.framework.Assert.assertEquals; import static org.hamcrest.CoreMatchers.*; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assert.*; @RunWith(WithTestDefaultsRunner.class) public class ViewTest { private View view; @Before public void setUp() throws Exception { view = new View(new Activity()); } @Test public void testHasEmptyLayoutParams() throws Exception { ViewGroup.LayoutParams layoutParams = view.getLayoutParams(); assertThat(layoutParams, notNullValue()); } @Test public void layout_shouldAffectWidthAndHeight() throws Exception { assertThat(view.getWidth(), equalTo(0)); assertThat(view.getHeight(), equalTo(0)); view.layout(100, 200, 303, 404); assertThat(view.getWidth(), equalTo(303 - 100)); assertThat(view.getHeight(), equalTo(404 - 200)); } @Test public void shouldFocus() throws Exception { final Transcript transcript = new Transcript(); view.setOnFocusChangeListener(new View.OnFocusChangeListener() { @Override public void onFocusChange(View v, boolean hasFocus) { transcript.add(hasFocus ? "Gained focus" : "Lost focus"); } }); assertFalse(view.isFocused()); assertFalse(view.hasFocus()); transcript.assertNoEventsSoFar(); view.requestFocus(); assertTrue(view.isFocused()); assertTrue(view.hasFocus()); transcript.assertEventsSoFar("Gained focus"); view.clearFocus(); assertFalse(view.isFocused()); assertFalse(view.hasFocus()); transcript.assertEventsSoFar("Lost focus"); } @Test public void shouldNotBeFocusableByDefault() throws Exception { assertFalse(view.isFocusable()); view.setFocusable(true); assertTrue(view.isFocusable()); } @Test public void shouldKnowIfThisOrAncestorsAreVisible() throws Exception { assertTrue(shadowOf(view).derivedIsVisible()); ViewGroup grandParent = new LinearLayout(null); ViewGroup parent = new LinearLayout(null); grandParent.addView(parent); parent.addView(view); grandParent.setVisibility(View.GONE); assertFalse(shadowOf(view).derivedIsVisible()); } @Test public void shouldInflateMergeRootedLayoutAndNotCreateReferentialLoops() throws Exception { LinearLayout root = new LinearLayout(null); root.inflate(new Activity(), R.layout.inner_merge, root); for (int i = 0; i < root.getChildCount(); i++) { View child = root.getChildAt(i); assertNotSame(root, child); } } @Test public void performLongClick_shouldClickOnView() throws Exception { TestOnLongClickListener clickListener = new TestOnLongClickListener(); view.setOnLongClickListener(clickListener); shadowOf(view).performLongClick(); assertTrue(clickListener.clicked); } @Test public void checkedClick_shouldClickOnView() throws Exception { TestOnClickListener clickListener = new TestOnClickListener(); view.setOnClickListener(clickListener); shadowOf(view).checkedPerformClick(); assertTrue(clickListener.clicked); } @Test(expected = RuntimeException.class) public void checkedClick_shouldThrowIfViewIsNotVisible() throws Exception { ViewGroup grandParent = new LinearLayout(null); ViewGroup parent = new LinearLayout(null); grandParent.addView(parent); parent.addView(view); grandParent.setVisibility(View.GONE); shadowOf(view).checkedPerformClick(); } @Test(expected = RuntimeException.class) public void checkedClick_shouldThrowIfViewIsDisabled() throws Exception { view.setEnabled(false); shadowOf(view).checkedPerformClick(); } @Test public void getBackground_shouldReturnNullIfNoBackgroundHasBeenSet() throws Exception { assertThat(view.getBackground(), nullValue()); } @Test public void shouldSetBackgroundColor() { view.setBackgroundColor(R.color.android_red); int intColor = view.getResources().getColor(R.color.android_red); assertThat((ColorDrawable) view.getBackground(), equalTo(new ColorDrawable(intColor))); } @Test public void shouldSetBackgroundResource() throws Exception { view.setBackgroundResource(R.drawable.an_image); assertThat(view.getBackground(), equalTo(view.getResources().getDrawable(R.drawable.an_image))); } @Test public void shouldRecordBackgroundColor() { int[] colors = {0, 1, 727}; for (int color : colors) { view.setBackgroundColor(color); assertThat(shadowOf(view).getBackgroundColor(), equalTo(color)); } } @Test public void shouldPostActionsToTheMessageQueue() throws Exception { Robolectric.pauseMainLooper(); TestRunnable runnable = new TestRunnable(); view.post(runnable); assertFalse(runnable.wasRun); Robolectric.unPauseMainLooper(); assertTrue(runnable.wasRun); } @Test public void shouldPostInvalidateDelayed() throws Exception { Robolectric.pauseMainLooper(); view.postInvalidateDelayed(100); ShadowView shadowView = shadowOf(view); assertFalse(shadowView.wasInvalidated()); Robolectric.unPauseMainLooper(); assertTrue(shadowView.wasInvalidated()); } @Test public void shouldPostActionsToTheMessageQueueWithDelay() throws Exception { Robolectric.pauseMainLooper(); TestRunnable runnable = new TestRunnable(); view.postDelayed(runnable, 1); assertFalse(runnable.wasRun); Robolectric.getUiThreadScheduler().advanceBy(1); assertTrue(runnable.wasRun); } @Test public void shouldSupportAllConstructors() throws Exception { new View(null); new View(null, null); new View(null, null, 0); } @Test public void shouldSetAnimation() throws Exception { Animation anim = new TestAnimation(); view.setAnimation(anim); assertThat(view.getAnimation(), sameInstance(anim)); } @Test public void shouldStartAndClearAnimation() throws Exception { Animation anim = new TestAnimation(); TestAnimationListener listener = new TestAnimationListener(); anim.setAnimationListener(listener); assertThat(listener.wasStartCalled, equalTo(false)); assertThat(listener.wasRepeatCalled, equalTo(false)); assertThat(listener.wasEndCalled, equalTo(false)); view.startAnimation(anim); assertThat(listener.wasStartCalled, equalTo(true)); assertThat(listener.wasRepeatCalled, equalTo(false)); assertThat(listener.wasEndCalled, equalTo(false)); view.clearAnimation(); assertThat(listener.wasStartCalled, equalTo(true)); assertThat(listener.wasRepeatCalled, equalTo(false)); assertThat(listener.wasEndCalled, equalTo(true)); } @Test public void shouldFindViewWithTag() { view.setTag("tagged"); assertThat(view.findViewWithTag("tagged"), sameInstance(view)); } @Test public void shouldFindViewWithTag_whenViewOverridesGetTag() throws Exception { View view = new View(Robolectric.application) { @Override public Object getTag() { return "blarg"; } }; assertThat(view.findViewWithTag("blarg"), sameInstance(view)); } @Test public void scrollTo_shouldStoreTheScrolledCoordinates() throws Exception { view.scrollTo(1, 2); assertThat(shadowOf(view).scrollToCoordinates, equalTo(new Point(1, 2))); } @Test public void shouldScrollTo() throws Exception { view.scrollTo(7, 6); assertEquals(7, view.getScrollX()); assertEquals(6, view.getScrollY()); } @Test public void shouldGetScrollXAndY() { assertEquals(0, view.getScrollX()); assertEquals(0, view.getScrollY()); } @Test public void getViewTreeObserver_shouldReturnTheSameObserverFromMultipleCalls() throws Exception { ViewTreeObserver observer = view.getViewTreeObserver(); assertThat(observer, instanceOf(ViewTreeObserver.class)); assertThat(view.getViewTreeObserver(), sameInstance(observer)); } @Test public void dispatchTouchEvent_sendsMotionEventToOnTouchEvent() throws Exception { TouchableView touchableView = new TouchableView(null); MotionEvent event = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 12f, 34f, 0); touchableView.dispatchTouchEvent(event); assertThat(touchableView.event, sameInstance(event)); view.dispatchTouchEvent(event); assertThat(shadowOf(view).getLastTouchEvent(), sameInstance(event)); } @Test public void dispatchTouchEvent_listensToTrueFromListener() throws Exception { view.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View view, MotionEvent motionEvent) { return true; } }); MotionEvent event = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 12f, 34f, 0); view.dispatchTouchEvent(event); assertThat(shadowOf(view).getLastTouchEvent(), nullValue()); } @Test public void dispatchTouchEvent_listensToFalseFromListener() throws Exception { view.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View view, MotionEvent motionEvent) { return false; } }); MotionEvent event = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 12f, 34f, 0); view.dispatchTouchEvent(event); assertThat(shadowOf(view).getLastTouchEvent(), sameInstance(event)); } @Test public void dispatchFocusEvent_sendsFocusToOnFocusChanged() throws Exception { FocusableView focusableView = new FocusableView(null); focusableView.requestFocus(); assertThat(focusableView.onFocusChangedWasCalled, equalTo(true)); } @Test public void test_nextFocusDownId() throws Exception { assertEquals(View.NO_ID, view.getNextFocusDownId()); view.setNextFocusDownId(R.id.icon); assertEquals(R.id.icon, view.getNextFocusDownId()); } @Test public void dispatchOnAnimationEnd() throws Exception { TestView view1 = new TestView(new Activity()); assertFalse(view1.onAnimationEndWasCalled); shadowOf(view1).finishedAnimation(); assertTrue(view1.onAnimationEndWasCalled); } @Test public void test_measuredDimension() { // View does not provide its own onMeasure implementation TestView view1 = new TestView(new Activity()); assertThat(view1.getHeight(), equalTo(0)); assertThat(view1.getWidth(), equalTo(0)); assertThat(view1.getMeasuredHeight(), equalTo(0)); assertThat(view1.getMeasuredWidth(), equalTo(0)); view1.measure( MeasureSpec.makeMeasureSpec(150, MeasureSpec.AT_MOST), MeasureSpec.makeMeasureSpec(300, MeasureSpec.AT_MOST) ); assertThat(view1.getHeight(), equalTo(0)); assertThat(view1.getWidth(), equalTo(0)); assertThat(view1.getMeasuredHeight(), equalTo(300)); assertThat(view1.getMeasuredWidth(), equalTo(150)); } @Test public void test_measuredDimensionCustomView() { // View provides its own onMeasure implementation TestView2 view2 = new TestView2(new Activity()); assertThat(view2.getHeight(), equalTo(0)); assertThat(view2.getWidth(), equalTo(0)); assertThat(view2.getMeasuredHeight(), equalTo(0)); assertThat(view2.getMeasuredWidth(), equalTo(0)); view2.measure( MeasureSpec.makeMeasureSpec(1000, MeasureSpec.AT_MOST), MeasureSpec.makeMeasureSpec(600, MeasureSpec.AT_MOST) ); assertThat(view2.getHeight(), equalTo(0)); assertThat(view2.getWidth(), equalTo(0)); assertThat(view2.getMeasuredHeight(), equalTo(400)); assertThat(view2.getMeasuredWidth(), equalTo(800)); } @Test public void testFilterTouchesWhenObscured() { assertFalse(view.getFilterTouchesWhenObscured()); view.setFilterTouchesWhenObscured(true); assertTrue(view.getFilterTouchesWhenObscured()); view.setFilterTouchesWhenObscured(false); assertFalse(view.getFilterTouchesWhenObscured()); } @Test public void testFilterTouchesWhenObscuredWhenLoadedFromXml() { LinearLayout root = new LinearLayout(null); ShadowView.inflate(new Activity(), R.layout.views, root); View defaultView = root.findViewById(R.id.default_view); assertFalse(defaultView.getFilterTouchesWhenObscured()); View filterFalseView = root.findViewById(R.id.filter_touches_false_view); assertFalse(filterFalseView.getFilterTouchesWhenObscured()); View filterTrueView = root.findViewById(R.id.filter_touches_true_view); assertTrue(filterTrueView.getFilterTouchesWhenObscured()); } @Test public void testClickable() { assertFalse(view.isClickable()); view.setClickable(true); assertTrue(view.isClickable()); view.setClickable(false); assertFalse(view.isClickable()); } @Test public void testClickableWhenLoadedFromXml() { LinearLayout root = new LinearLayout(null); ShadowView.inflate(new Activity(), R.layout.views, root); View defaultView = root.findViewById(R.id.default_view); assertFalse(defaultView.isClickable()); View clickableFalseView = root.findViewById(R.id.clickable_false_view); assertFalse(clickableFalseView.isClickable()); View clickableTrueView = root.findViewById(R.id.clickable_true_view); assertTrue(clickableTrueView.isClickable()); } @Test public void testFocusable() { assertFalse(view.isFocusable()); view.setFocusable(true); assertTrue(view.isFocusable()); view.setFocusable(false); assertFalse(view.isFocusable()); } @Test public void testFocusableWhenLoadedFromXml() { LinearLayout root = new LinearLayout(null); ShadowView.inflate(new Activity(), R.layout.views, root); View defaultView = root.findViewById(R.id.default_view); assertFalse(defaultView.isFocusable()); View focusableFalseView = root.findViewById(R.id.focusable_false_view); assertFalse(focusableFalseView.isFocusable()); View focusableTrueView = root.findViewById(R.id.focusable_true_view); assertTrue(focusableTrueView.isFocusable()); } private static class TestAnimation extends Animation { } private static class TouchableView extends View { MotionEvent event; public TouchableView(Context context) { super(context); } @Override public boolean onTouchEvent(MotionEvent event) { this.event = event; return false; } } private static class TestView extends View { boolean onAnimationEndWasCalled; public TestView(Context context) { super(context); } @Override protected void onAnimationEnd() { super.onAnimationEnd(); onAnimationEndWasCalled = true; } } private static class FocusableView extends View { boolean onFocusChangedWasCalled; public FocusableView(Context context) { super(context); } @Override protected void onFocusChanged(boolean gainFocus, int direction, Rect previouslyFocusedRect) { super.onFocusChanged(gainFocus, direction, previouslyFocusedRect); onFocusChangedWasCalled = true; } } private static class TestView2 extends View { public TestView2(Context context) { super(context); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(800, 400); } } }