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.support.test.InstrumentationRegistry; 20import android.support.test.filters.SmallTest; 21import android.support.test.runner.AndroidJUnit4; 22import android.text.InputType; 23import android.util.KeyUtils; 24import android.view.KeyEvent; 25import android.widget.EditText; 26import android.widget.TextView.BufferType; 27import org.junit.Before; 28import org.junit.Test; 29import 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) 39public 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