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