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