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