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