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.text.method;
18
19import android.app.Activity;
20import android.test.suitebuilder.annotation.SmallTest;
21import android.test.suitebuilder.annotation.Suppress;
22import android.text.InputType;
23import android.text.method.BaseKeyListener;
24import android.text.method.KeyListenerTestCase;
25import android.view.KeyEvent;
26import android.widget.EditText;
27import android.widget.TextView.BufferType;
28
29/**
30 * Test forward delete key handling of  {@link android.text.method.BaseKeyListener}.
31 *
32 * Only contains edge cases. For normal cases, see {@see android.text.method.cts.ForwardDeleteTest}.
33 * TODO: introduce test cases for surrogate pairs and replacement span.
34 */
35public class ForwardDeleteTest extends KeyListenerTestCase {
36    private static final BaseKeyListener mKeyListener = new BaseKeyListener() {
37        public int getInputType() {
38            return InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_NORMAL;
39        }
40    };
41
42    // Sync the state to the TextView and call onKeyDown with KEYCODE_FORWARD_DEL key event.
43    // Then update the state to the result of TextView.
44    private void forwardDelete(final EditorState state, int modifiers) {
45        mActivity.runOnUiThread(new Runnable() {
46            public void run() {
47                mTextView.setText(state.mText, BufferType.EDITABLE);
48                mTextView.setKeyListener(mKeyListener);
49                mTextView.setSelection(state.mSelectionStart, state.mSelectionEnd);
50            }
51        });
52        mInstrumentation.waitForIdleSync();
53        assertTrue(mTextView.hasWindowFocus());
54
55        final KeyEvent keyEvent = getKey(KeyEvent.KEYCODE_FORWARD_DEL, modifiers);
56        mActivity.runOnUiThread(new Runnable() {
57            public void run() {
58                mTextView.onKeyDown(keyEvent.getKeyCode(), keyEvent);
59            }
60        });
61        mInstrumentation.waitForIdleSync();
62
63        state.mText = mTextView.getText();
64        state.mSelectionStart = mTextView.getSelectionStart();
65        state.mSelectionEnd = mTextView.getSelectionEnd();
66    }
67
68    @SmallTest
69    public void testCombiningEnclosingKeycaps() {
70        EditorState state = new EditorState();
71
72        // multiple COMBINING ENCLOSING KEYCAP
73        state.setByString("| '1' U+20E3 U+20E3");
74        forwardDelete(state, 0);
75        state.assertEquals("|");
76
77        // Isolated COMBINING ENCLOSING KEYCAP
78        state.setByString("| U+20E3");
79        forwardDelete(state, 0);
80        state.assertEquals("|");
81
82        // Isolated multiple COMBINING ENCLOSING KEYCAP
83        state.setByString("| U+20E3 U+20E3");
84        forwardDelete(state, 0);
85        state.assertEquals("|");
86    }
87
88    @SmallTest
89    public void testVariationSelector() {
90        EditorState state = new EditorState();
91
92        // Isolated variation selectors
93        state.setByString("| U+FE0F");
94        forwardDelete(state, 0);
95        state.assertEquals("|");
96
97        state.setByString("| U+E0100");
98        forwardDelete(state, 0);
99        state.assertEquals("|");
100
101        // Isolated multiple variation selectors
102        state.setByString("| U+FE0F U+FE0F");
103        forwardDelete(state, 0);
104        state.assertEquals("|");
105
106        state.setByString("| U+FE0F U+E0100");
107        forwardDelete(state, 0);
108        state.assertEquals("|");
109
110        state.setByString("| U+E0100 U+FE0F");
111        forwardDelete(state, 0);
112        state.assertEquals("|");
113
114        state.setByString("| U+E0100 U+E0100");
115        forwardDelete(state, 0);
116        state.assertEquals("|");
117
118        // Multiple variation selectors
119        state.setByString("| '#' U+FE0F U+FE0F");
120        forwardDelete(state, 0);
121        state.assertEquals("|");
122
123        state.setByString("| '#' U+FE0F U+E0100");
124        forwardDelete(state, 0);
125        state.assertEquals("|");
126
127        state.setByString("| U+845B U+E0100 U+FE0F");
128        forwardDelete(state, 0);
129        state.assertEquals("|");
130
131        state.setByString("| U+845B U+E0100 U+E0100");
132        forwardDelete(state, 0);
133        state.assertEquals("|");
134    }
135
136    @SmallTest
137    public void testEmojiZeroWidthJoinerSequence() {
138        EditorState state = new EditorState();
139
140        // U+200D is ZERO WIDTH JOINER.
141        state.setByString("| U+1F441 U+200D U+1F5E8");
142        forwardDelete(state, 0);
143        state.assertEquals("|");
144
145        state.setByString("| U+1F468 U+200D U+2764 U+FE0F U+200D U+1F48B U+200D U+1F468");
146        forwardDelete(state, 0);
147        state.assertEquals("|");
148
149        // End with ZERO WIDTH JOINER
150        state.setByString("| U+1F441 U+200D");
151        forwardDelete(state, 0);
152        state.assertEquals("|");
153
154        // Start with ZERO WIDTH JOINER
155        state.setByString("| U+200D U+1F5E8");
156        forwardDelete(state, 0);
157        state.assertEquals("| U+1F5E8");
158        forwardDelete(state, 0);
159        state.assertEquals("|");
160
161        // Multiple ZERO WIDTH JOINER
162        state.setByString("| U+1F441 U+200D U+200D U+1F5E8");
163        forwardDelete(state, 0);
164        state.assertEquals("| U+1F5E8");
165        forwardDelete(state, 0);
166        state.assertEquals("|");
167
168        // Isolated ZERO WIDTH JOINER
169        state.setByString("| U+200D");
170        forwardDelete(state, 0);
171        state.assertEquals("|");
172
173        // Isolated multiple ZERO WIDTH JOINER
174        state.setByString("| U+200D U+200D");
175        forwardDelete(state, 0);
176        state.assertEquals("|");
177    }
178
179    @SmallTest
180    public void testFlags() {
181        EditorState state = new EditorState();
182
183        // Isolated regional indicator symbol
184        state.setByString("| U+1F1FA");
185        forwardDelete(state, 0);
186        state.assertEquals("|");
187
188        // Odd numbered regional indicator symbols
189        state.setByString("| U+1F1FA U+1F1F8 U+1F1FA");
190        forwardDelete(state, 0);
191        state.assertEquals("| U+1F1FA");
192        forwardDelete(state, 0);
193        state.assertEquals("|");
194    }
195
196    @SmallTest
197    public void testEmojiModifier() {
198        EditorState state = new EditorState();
199
200        // U+1F3FB is EMOJI MODIFIER FITZPATRICK TYPE-1-2.
201        state.setByString("| U+1F466 U+1F3FB");
202        forwardDelete(state, 0);
203        state.assertEquals("|");
204
205        // Isolated emoji modifier
206        state.setByString("| U+1F3FB");
207        forwardDelete(state, 0);
208        state.assertEquals("|");
209
210        // Isolated multiple emoji modifier
211        state.setByString("| U+1F3FB U+1F3FB");
212        forwardDelete(state, 0);
213        state.assertEquals("| U+1F3FB");
214        forwardDelete(state, 0);
215        state.assertEquals("|");
216
217        // Multiple emoji modifiers
218        state.setByString("| U+1F466 U+1F3FB U+1F3FB");
219        forwardDelete(state, 0);
220        state.assertEquals("| U+1F3FB");
221        forwardDelete(state, 0);
222        state.assertEquals("|");
223    }
224
225    @SmallTest
226    public void testMixedEdgeCases() {
227        EditorState state = new EditorState();
228
229        // COMBINING ENCLOSING KEYCAP + variation selector
230        state.setByString("| '1' U+20E3 U+FE0F");
231        forwardDelete(state, 0);
232        state.assertEquals("|");
233
234        // Variation selector + COMBINING ENCLOSING KEYCAP
235        state.setByString("| U+2665 U+FE0F U+20E3");
236        forwardDelete(state, 0);
237        state.assertEquals("|");
238
239        // COMBINING ENCLOSING KEYCAP + ending with ZERO WIDTH JOINER
240        state.setByString("| '1' U+20E3 U+200D");
241        forwardDelete(state, 0);
242        state.assertEquals("|");
243
244        // COMBINING ENCLOSING KEYCAP + ZERO WIDTH JOINER
245        state.setByString("| '1' U+20E3 U+200D U+1F5E8");
246        forwardDelete(state, 0);
247        state.assertEquals("| U+1F5E8 ");
248
249        // Start with ZERO WIDTH JOINER + COMBINING ENCLOSING KEYCAP
250        state.setByString("| U+200D U+20E3");
251        forwardDelete(state, 0);
252        state.assertEquals("|");
253
254        // ZERO WIDTH JOINER + COMBINING ENCLOSING KEYCAP
255        state.setByString("| U+1F441 U+200D U+20E3");
256        forwardDelete(state, 0);
257        state.assertEquals("|");
258
259        // COMBINING ENCLOSING KEYCAP + regional indicator symbol
260        state.setByString("| '1' U+20E3 U+1F1FA");
261        forwardDelete(state, 0);
262        state.assertEquals("| U+1F1FA");
263        forwardDelete(state, 0);
264        state.assertEquals("|");
265
266        // Regional indicator symbol + COMBINING ENCLOSING KEYCAP
267        state.setByString("| U+1F1FA U+20E3");
268        forwardDelete(state, 0);
269        state.assertEquals("|");
270
271        // COMBINING ENCLOSING KEYCAP + emoji modifier
272        state.setByString("| '1' U+20E3 U+1F3FB");
273        forwardDelete(state, 0);
274        state.assertEquals("| U+1F3FB");
275
276        // Emoji modifier + COMBINING ENCLOSING KEYCAP
277        state.setByString("| U+1F466 U+1F3FB U+20E3");
278        forwardDelete(state, 0);
279        state.assertEquals("|");
280
281        // Variation selector + end with ZERO WIDTH JOINER
282        state.setByString("| U+2665 U+FE0F U+200D");
283        forwardDelete(state, 0);
284        state.assertEquals("|");
285
286        // Variation selector + ZERO WIDTH JOINER
287        state.setByString("| U+1F469 U+200D U+2764 U+FE0F U+200D U+1F469");
288        forwardDelete(state, 0);
289        state.assertEquals("|");
290
291        // Start with ZERO WIDTH JOINER + variation selector
292        state.setByString("| U+200D U+FE0F");
293        forwardDelete(state, 0);
294        state.assertEquals("|");
295
296        // ZERO WIDTH JOINER + variation selector
297        state.setByString("| U+1F469 U+200D U+FE0F");
298        forwardDelete(state, 0);
299        state.assertEquals("|");
300
301        // Variation selector + regional indicator symbol
302        state.setByString("| U+2665 U+FE0F U+1F1FA");
303        forwardDelete(state, 0);
304        state.assertEquals("| U+1F1FA");
305        forwardDelete(state, 0);
306        state.assertEquals("|");
307
308        // Regional indicator symbol + variation selector
309        state.setByString("| U+1F1FA U+FE0F");
310        forwardDelete(state, 0);
311        state.assertEquals("|");
312
313        // Variation selector + emoji modifier
314        state.setByString("| U+2665 U+FE0F U+1F3FB");
315        forwardDelete(state, 0);
316        state.assertEquals("| U+1F3FB");
317
318        // Emoji modifier + variation selector
319        state.setByString("| U+1F466 U+1F3FB U+FE0F");
320        forwardDelete(state, 0);
321        state.assertEquals("|");
322
323        // Start with ZERO WIDTH JOINER + regional indicator symbol
324        state.setByString("| U+200D U+1F1FA");
325        forwardDelete(state, 0);
326        state.assertEquals("| U+1F1FA");
327        forwardDelete(state, 0);
328        state.assertEquals("|");
329
330        // ZERO WIDTH JOINER + regional indicator symbol
331        state.setByString("| U+1F469 U+200D U+1F1FA");
332        forwardDelete(state, 0);
333        state.assertEquals("| U+1F1FA");
334
335        // Regional indicator symbol + end with ZERO WIDTH JOINER
336        state.setByString("| U+1F1FA U+200D");
337        forwardDelete(state, 0);
338        state.assertEquals("|");
339
340        // Regional indicator symbol + ZERO WIDTH JOINER
341        state.setByString("| U+1F1FA U+200D U+1F469");
342        forwardDelete(state, 0);
343        state.assertEquals("| U+1F469");
344        forwardDelete(state, 0);
345        state.assertEquals("|");
346
347        // Start with ZERO WIDTH JOINER + emoji modifier
348        state.setByString("| U+200D U+1F3FB");
349        forwardDelete(state, 0);
350        state.assertEquals("| U+1F3FB");
351
352        // ZERO WIDTH JOINER + emoji modifier
353        state.setByString("| U+1F469 U+200D U+1F3FB");
354        forwardDelete(state, 0);
355        state.assertEquals("| U+1F3FB");
356
357        // Emoji modifier + end with ZERO WIDTH JOINER
358        state.setByString("| U+1F466 U+1F3FB U+200D");
359        forwardDelete(state, 0);
360        state.assertEquals("|");
361
362        // Emoji modifier + ZERO WIDTH JOINER
363        state.setByString("| U+1F466 U+1F3FB U+200D U+1F469");
364        forwardDelete(state, 0);
365        state.assertEquals("| U+1F469");
366        forwardDelete(state, 0);
367        state.assertEquals("|");
368
369        // Regional indicator symbol + emoji modifier
370        state.setByString("| U+1F1FA U+1F3FB");
371        forwardDelete(state, 0);
372        state.assertEquals("| U+1F3FB");
373        forwardDelete(state, 0);
374        state.assertEquals("|");
375
376        // Emoji modifier + regional indicator symbol
377        state.setByString("| U+1F466 U+1F3FB U+1F1FA");
378        forwardDelete(state, 0);
379        state.assertEquals("| U+1F1FA");
380        forwardDelete(state, 0);
381        state.assertEquals("|");
382    }
383}
384