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