1/*
2 * Copyright (C) 2016 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.widget;
18
19import android.test.ActivityInstrumentationTestCase2;
20import android.test.suitebuilder.annotation.SmallTest;
21import android.view.Choreographer;
22import android.view.ViewGroup;
23
24import java.util.concurrent.CountDownLatch;
25import java.util.concurrent.TimeUnit;
26
27import static android.support.test.espresso.Espresso.onView;
28import static android.support.test.espresso.action.ViewActions.click;
29import static android.widget.espresso.TextViewAssertions.hasInsertionPointerOnLeft;
30import static android.widget.espresso.TextViewAssertions.hasInsertionPointerOnRight;
31import static org.hamcrest.MatcherAssert.assertThat;
32import static org.hamcrest.Matchers.equalTo;
33import static org.hamcrest.Matchers.isEmptyString;
34import static org.hamcrest.Matchers.nullValue;
35import static org.hamcrest.Matchers.sameInstance;
36
37public class EditorCursorTest extends ActivityInstrumentationTestCase2<TextViewActivity> {
38
39
40    private final static String LTR_STRING = "aaaaaaaaaaaaaaaaaaaaaa";
41    private final static String LTR_HINT = "hint";
42    private final static String RTL_STRING = "مرحبا الروبوت مرحبا الروبوت مرحبا الروبوت";
43    private final static String RTL_HINT = "الروبوت";
44    private final static int CURSOR_BLINK_MS = 500;
45
46    private EditText mEditText;
47
48    public EditorCursorTest() {
49        super(TextViewActivity.class);
50    }
51
52    @Override
53    protected void setUp() throws Exception {
54        super.setUp();
55        mEditText = new EditText(getActivity());
56        mEditText.setTextSize(30);
57        mEditText.setSingleLine(true);
58        mEditText.setLines(1);
59        mEditText.setPadding(15, 15, 15, 15);
60        ViewGroup.LayoutParams editTextLayoutParams = new ViewGroup.LayoutParams(200,
61                ViewGroup.LayoutParams.WRAP_CONTENT);
62
63        mEditText.setLayoutParams(editTextLayoutParams);
64
65        final FrameLayout layout = new FrameLayout(getActivity());
66        ViewGroup.LayoutParams layoutParams = new ViewGroup.LayoutParams(
67                ViewGroup.LayoutParams.MATCH_PARENT,
68                ViewGroup.LayoutParams.MATCH_PARENT);
69        layout.setLayoutParams(layoutParams);
70        layout.addView(mEditText);
71
72        getActivity().runOnUiThread(new Runnable() {
73            @Override
74            public void run() {
75                getActivity().setContentView(layout);
76            }
77        });
78        getInstrumentation().waitForIdleSync();
79        onView(sameInstance(mEditText)).perform(click());
80    }
81
82    @SmallTest
83    public void testCursorIsInViewBoundariesWhenOnRightForLtr() {
84        // Asserts that when an EditText has LTR text, and cursor is at the end (right),
85        // cursor is drawn to the right edge of the view
86        setEditTextText(LTR_STRING, LTR_STRING.length());
87
88        onView(sameInstance(mEditText)).check(hasInsertionPointerOnRight());
89    }
90
91    @SmallTest
92    public void testCursorIsInViewBoundariesWhenOnLeftForLtr() {
93        // Asserts that when an EditText has LTR text, and cursor is at the beginning,
94        // cursor is drawn to the left edge of the view
95        setEditTextText(LTR_STRING, 0);
96
97        onView(sameInstance(mEditText)).check(hasInsertionPointerOnLeft());
98    }
99
100    @SmallTest
101    public void testCursorIsInViewBoundariesWhenOnRightForRtl() {
102        // Asserts that when an EditText has RTL text, and cursor is at the end,
103        // cursor is drawn to the left edge of the view
104        setEditTextText(RTL_STRING, 0);
105
106        onView(sameInstance(mEditText)).check(hasInsertionPointerOnRight());
107    }
108
109    @SmallTest
110    public void testCursorIsInViewBoundariesWhenOnLeftForRtl() {
111        // Asserts that when an EditText has RTL text, and cursor is at the beginning,
112        // cursor is drawn to the right edge of the view
113        setEditTextText(RTL_STRING, RTL_STRING.length());
114
115        onView(sameInstance(mEditText)).check(hasInsertionPointerOnLeft());
116    }
117
118    /* Tests for cursor positioning with hint */
119    @SmallTest
120    public void testCursorIsOnLeft_withFirstStrongLtrAlgorithm() {
121        setEditTextHint(null, TextView.TEXT_DIRECTION_FIRST_STRONG_LTR, 0);
122        assertThat(mEditText.getText().toString(), isEmptyString());
123        assertThat(mEditText.getHint(), nullValue());
124
125        onView(sameInstance(mEditText)).check(hasInsertionPointerOnLeft());
126
127        setEditTextHint(RTL_HINT, TextView.TEXT_DIRECTION_FIRST_STRONG_LTR, 0);
128        assertThat(mEditText.getText().toString(), isEmptyString());
129
130        onView(sameInstance(mEditText)).check(hasInsertionPointerOnLeft());
131
132        setEditTextHint(LTR_HINT, TextView.TEXT_DIRECTION_FIRST_STRONG_LTR, 0);
133        assertThat(mEditText.getText().toString(), isEmptyString());
134
135        onView(sameInstance(mEditText)).check(hasInsertionPointerOnLeft());
136    }
137
138    @SmallTest
139    public void testCursorIsOnRight_withFirstStrongRtlAlgorithm() {
140        setEditTextHint(null, TextView.TEXT_DIRECTION_FIRST_STRONG_RTL, 0);
141        assertThat(mEditText.getText().toString(), isEmptyString());
142        assertThat(mEditText.getHint(), nullValue());
143
144        onView(sameInstance(mEditText)).check(hasInsertionPointerOnRight());
145
146        setEditTextHint(LTR_HINT, TextView.TEXT_DIRECTION_FIRST_STRONG_RTL, 0);
147        assertThat(mEditText.getText().toString(), isEmptyString());
148
149        onView(sameInstance(mEditText)).check(hasInsertionPointerOnRight());
150
151        setEditTextHint(RTL_HINT, TextView.TEXT_DIRECTION_FIRST_STRONG_RTL, 0);
152        assertThat(mEditText.getText().toString(), isEmptyString());
153
154        onView(sameInstance(mEditText)).check(hasInsertionPointerOnRight());
155    }
156
157    @SmallTest
158    public void testCursorIsOnLeft_withLtrAlgorithm() {
159        setEditTextHint(null, TextView.TEXT_DIRECTION_LTR, 0);
160        assertThat(mEditText.getText().toString(), isEmptyString());
161        assertThat(mEditText.getHint(), nullValue());
162
163        onView(sameInstance(mEditText)).check(hasInsertionPointerOnLeft());
164
165        setEditTextHint(RTL_HINT, TextView.TEXT_DIRECTION_LTR, 0);
166        assertThat(mEditText.getText().toString(), isEmptyString());
167
168        onView(sameInstance(mEditText)).check(hasInsertionPointerOnLeft());
169
170        setEditTextHint(LTR_HINT, TextView.TEXT_DIRECTION_LTR, 0);
171        assertThat(mEditText.getText().toString(), isEmptyString());
172
173        onView(sameInstance(mEditText)).check(hasInsertionPointerOnLeft());
174    }
175
176    @SmallTest
177    public void testCursorIsOnRight_withRtlAlgorithm() {
178        setEditTextHint(null, TextView.TEXT_DIRECTION_RTL, 0);
179        assertThat(mEditText.getText().toString(), isEmptyString());
180        assertThat(mEditText.getHint(), nullValue());
181
182        onView(sameInstance(mEditText)).check(hasInsertionPointerOnRight());
183
184        setEditTextHint(LTR_HINT, TextView.TEXT_DIRECTION_RTL, 0);
185        assertThat(mEditText.getText().toString(), isEmptyString());
186
187        onView(sameInstance(mEditText)).check(hasInsertionPointerOnRight());
188
189        setEditTextHint(RTL_HINT, TextView.TEXT_DIRECTION_RTL, 0);
190        assertThat(mEditText.getText().toString(), isEmptyString());
191
192        onView(sameInstance(mEditText)).check(hasInsertionPointerOnRight());
193    }
194
195    private void setEditTextProperties(final String text, final String hint,
196            final Integer textDirection, final Integer selection) {
197        getActivity().runOnUiThread(new Runnable() {
198            @Override
199            public void run() {
200                if (textDirection != null) mEditText.setTextDirection(textDirection);
201                if (text != null) mEditText.setText(text);
202                if (hint != null) mEditText.setHint(hint);
203                if (selection != null) mEditText.setSelection(selection);
204            }
205        });
206        getInstrumentation().waitForIdleSync();
207
208        // wait for cursor to be drawn. updateCursorPositions function is called during draw() and
209        // only when cursor is visible during blink.
210        final CountDownLatch latch = new CountDownLatch(1);
211        mEditText.postOnAnimationDelayed(new Runnable() {
212            @Override
213            public void run() {
214                latch.countDown();
215            }
216        }, CURSOR_BLINK_MS);
217        try {
218            assertThat("Problem while waiting for the cursor to blink",
219                    latch.await(10, TimeUnit.SECONDS), equalTo(true));
220        } catch (Exception e) {
221            fail("Problem while waiting for the cursor to blink");
222        }
223    }
224
225    private void setEditTextHint(final String hint, final int textDirection, final int selection) {
226        setEditTextProperties(null, hint, textDirection, selection);
227    }
228
229    private void setEditTextText(final String text, final Integer selection) {
230        setEditTextProperties(text, null, null, selection);
231    }
232}
233