/* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.widget; import android.test.ActivityInstrumentationTestCase2; import android.test.suitebuilder.annotation.SmallTest; import android.view.Choreographer; import android.view.ViewGroup; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import static android.support.test.espresso.Espresso.onView; import static android.support.test.espresso.action.ViewActions.click; import static android.widget.espresso.TextViewAssertions.hasInsertionPointerOnLeft; import static android.widget.espresso.TextViewAssertions.hasInsertionPointerOnRight; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.isEmptyString; import static org.hamcrest.Matchers.nullValue; import static org.hamcrest.Matchers.sameInstance; public class EditorCursorTest extends ActivityInstrumentationTestCase2<TextViewActivity> { private final static String LTR_STRING = "aaaaaaaaaaaaaaaaaaaaaa"; private final static String LTR_HINT = "hint"; private final static String RTL_STRING = "مرحبا الروبوت مرحبا الروبوت مرحبا الروبوت"; private final static String RTL_HINT = "الروبوت"; private final static int CURSOR_BLINK_MS = 500; private EditText mEditText; public EditorCursorTest() { super(TextViewActivity.class); } @Override protected void setUp() throws Exception { super.setUp(); mEditText = new EditText(getActivity()); mEditText.setTextSize(30); mEditText.setSingleLine(true); mEditText.setLines(1); mEditText.setPadding(15, 15, 15, 15); ViewGroup.LayoutParams editTextLayoutParams = new ViewGroup.LayoutParams(200, ViewGroup.LayoutParams.WRAP_CONTENT); mEditText.setLayoutParams(editTextLayoutParams); final FrameLayout layout = new FrameLayout(getActivity()); ViewGroup.LayoutParams layoutParams = new ViewGroup.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT); layout.setLayoutParams(layoutParams); layout.addView(mEditText); getActivity().runOnUiThread(new Runnable() { @Override public void run() { getActivity().setContentView(layout); } }); getInstrumentation().waitForIdleSync(); onView(sameInstance(mEditText)).perform(click()); } @SmallTest public void testCursorIsInViewBoundariesWhenOnRightForLtr() { // Asserts that when an EditText has LTR text, and cursor is at the end (right), // cursor is drawn to the right edge of the view setEditTextText(LTR_STRING, LTR_STRING.length()); onView(sameInstance(mEditText)).check(hasInsertionPointerOnRight()); } @SmallTest public void testCursorIsInViewBoundariesWhenOnLeftForLtr() { // Asserts that when an EditText has LTR text, and cursor is at the beginning, // cursor is drawn to the left edge of the view setEditTextText(LTR_STRING, 0); onView(sameInstance(mEditText)).check(hasInsertionPointerOnLeft()); } @SmallTest public void testCursorIsInViewBoundariesWhenOnRightForRtl() { // Asserts that when an EditText has RTL text, and cursor is at the end, // cursor is drawn to the left edge of the view setEditTextText(RTL_STRING, 0); onView(sameInstance(mEditText)).check(hasInsertionPointerOnRight()); } @SmallTest public void testCursorIsInViewBoundariesWhenOnLeftForRtl() { // Asserts that when an EditText has RTL text, and cursor is at the beginning, // cursor is drawn to the right edge of the view setEditTextText(RTL_STRING, RTL_STRING.length()); onView(sameInstance(mEditText)).check(hasInsertionPointerOnLeft()); } /* Tests for cursor positioning with hint */ @SmallTest public void testCursorIsOnLeft_withFirstStrongLtrAlgorithm() { setEditTextHint(null, TextView.TEXT_DIRECTION_FIRST_STRONG_LTR, 0); assertThat(mEditText.getText().toString(), isEmptyString()); assertThat(mEditText.getHint(), nullValue()); onView(sameInstance(mEditText)).check(hasInsertionPointerOnLeft()); setEditTextHint(RTL_HINT, TextView.TEXT_DIRECTION_FIRST_STRONG_LTR, 0); assertThat(mEditText.getText().toString(), isEmptyString()); onView(sameInstance(mEditText)).check(hasInsertionPointerOnLeft()); setEditTextHint(LTR_HINT, TextView.TEXT_DIRECTION_FIRST_STRONG_LTR, 0); assertThat(mEditText.getText().toString(), isEmptyString()); onView(sameInstance(mEditText)).check(hasInsertionPointerOnLeft()); } @SmallTest public void testCursorIsOnRight_withFirstStrongRtlAlgorithm() { setEditTextHint(null, TextView.TEXT_DIRECTION_FIRST_STRONG_RTL, 0); assertThat(mEditText.getText().toString(), isEmptyString()); assertThat(mEditText.getHint(), nullValue()); onView(sameInstance(mEditText)).check(hasInsertionPointerOnRight()); setEditTextHint(LTR_HINT, TextView.TEXT_DIRECTION_FIRST_STRONG_RTL, 0); assertThat(mEditText.getText().toString(), isEmptyString()); onView(sameInstance(mEditText)).check(hasInsertionPointerOnRight()); setEditTextHint(RTL_HINT, TextView.TEXT_DIRECTION_FIRST_STRONG_RTL, 0); assertThat(mEditText.getText().toString(), isEmptyString()); onView(sameInstance(mEditText)).check(hasInsertionPointerOnRight()); } @SmallTest public void testCursorIsOnLeft_withLtrAlgorithm() { setEditTextHint(null, TextView.TEXT_DIRECTION_LTR, 0); assertThat(mEditText.getText().toString(), isEmptyString()); assertThat(mEditText.getHint(), nullValue()); onView(sameInstance(mEditText)).check(hasInsertionPointerOnLeft()); setEditTextHint(RTL_HINT, TextView.TEXT_DIRECTION_LTR, 0); assertThat(mEditText.getText().toString(), isEmptyString()); onView(sameInstance(mEditText)).check(hasInsertionPointerOnLeft()); setEditTextHint(LTR_HINT, TextView.TEXT_DIRECTION_LTR, 0); assertThat(mEditText.getText().toString(), isEmptyString()); onView(sameInstance(mEditText)).check(hasInsertionPointerOnLeft()); } @SmallTest public void testCursorIsOnRight_withRtlAlgorithm() { setEditTextHint(null, TextView.TEXT_DIRECTION_RTL, 0); assertThat(mEditText.getText().toString(), isEmptyString()); assertThat(mEditText.getHint(), nullValue()); onView(sameInstance(mEditText)).check(hasInsertionPointerOnRight()); setEditTextHint(LTR_HINT, TextView.TEXT_DIRECTION_RTL, 0); assertThat(mEditText.getText().toString(), isEmptyString()); onView(sameInstance(mEditText)).check(hasInsertionPointerOnRight()); setEditTextHint(RTL_HINT, TextView.TEXT_DIRECTION_RTL, 0); assertThat(mEditText.getText().toString(), isEmptyString()); onView(sameInstance(mEditText)).check(hasInsertionPointerOnRight()); } private void setEditTextProperties(final String text, final String hint, final Integer textDirection, final Integer selection) { getActivity().runOnUiThread(new Runnable() { @Override public void run() { if (textDirection != null) mEditText.setTextDirection(textDirection); if (text != null) mEditText.setText(text); if (hint != null) mEditText.setHint(hint); if (selection != null) mEditText.setSelection(selection); } }); getInstrumentation().waitForIdleSync(); // wait for cursor to be drawn. updateCursorPositions function is called during draw() and // only when cursor is visible during blink. final CountDownLatch latch = new CountDownLatch(1); mEditText.postOnAnimationDelayed(new Runnable() { @Override public void run() { latch.countDown(); } }, CURSOR_BLINK_MS); try { assertThat("Problem while waiting for the cursor to blink", latch.await(10, TimeUnit.SECONDS), equalTo(true)); } catch (Exception e) { fail("Problem while waiting for the cursor to blink"); } } private void setEditTextHint(final String hint, final int textDirection, final int selection) { setEditTextProperties(null, hint, textDirection, selection); } private void setEditTextText(final String text, final Integer selection) { setEditTextProperties(text, null, null, selection); } }