EmojiCompatTest.java revision 34f638e630f75357a5f706f387ee9099c97af26b
1/* 2 * Copyright (C) 2017 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 */ 16package android.support.text.emoji; 17 18import static android.support.text.emoji.TestConfigBuilder.TestConfig; 19import static android.support.text.emoji.TestConfigBuilder.WaitingDataLoader; 20import static android.support.text.emoji.TestConfigBuilder.config; 21import static android.support.text.emoji.util.Emoji.CHAR_DEFAULT_EMOJI_STYLE; 22import static android.support.text.emoji.util.Emoji.CHAR_DEFAULT_TEXT_STYLE; 23import static android.support.text.emoji.util.Emoji.CHAR_DIGIT; 24import static android.support.text.emoji.util.Emoji.CHAR_FITZPATRICK; 25import static android.support.text.emoji.util.Emoji.CHAR_VS_EMOJI; 26import static android.support.text.emoji.util.Emoji.CHAR_VS_TEXT; 27import static android.support.text.emoji.util.Emoji.DEFAULT_TEXT_STYLE; 28import static android.support.text.emoji.util.Emoji.EMOJI_ASTERISK_KEYCAP; 29import static android.support.text.emoji.util.Emoji.EMOJI_DIGIT_ES; 30import static android.support.text.emoji.util.Emoji.EMOJI_DIGIT_ES_KEYCAP; 31import static android.support.text.emoji.util.Emoji.EMOJI_DIGIT_KEYCAP; 32import static android.support.text.emoji.util.Emoji.EMOJI_FLAG; 33import static android.support.text.emoji.util.Emoji.EMOJI_GENDER; 34import static android.support.text.emoji.util.Emoji.EMOJI_GENDER_WITHOUT_VS; 35import static android.support.text.emoji.util.Emoji.EMOJI_REGIONAL_SYMBOL; 36import static android.support.text.emoji.util.Emoji.EMOJI_SINGLE_CODEPOINT; 37import static android.support.text.emoji.util.Emoji.EMOJI_SKIN_MODIFIER; 38import static android.support.text.emoji.util.Emoji.EMOJI_SKIN_MODIFIER_TYPE_ONE; 39import static android.support.text.emoji.util.Emoji.EMOJI_SKIN_MODIFIER_WITH_VS; 40import static android.support.text.emoji.util.Emoji.EMOJI_UNKNOWN_FLAG; 41import static android.support.text.emoji.util.Emoji.EMOJI_WITH_ZWJ; 42import static android.support.text.emoji.util.EmojiMatcher.hasEmoji; 43import static android.support.text.emoji.util.EmojiMatcher.hasEmojiAt; 44import static android.support.text.emoji.util.EmojiMatcher.hasEmojiCount; 45import static android.support.text.emoji.util.KeyboardUtil.del; 46 47import static junit.framework.TestCase.assertFalse; 48 49import static org.hamcrest.Matchers.instanceOf; 50import static org.hamcrest.Matchers.not; 51import static org.junit.Assert.assertEquals; 52import static org.junit.Assert.assertNotNull; 53import static org.junit.Assert.assertNull; 54import static org.junit.Assert.assertSame; 55import static org.junit.Assert.assertThat; 56import static org.junit.Assert.assertTrue; 57import static org.mockito.Matchers.any; 58import static org.mockito.Matchers.anyInt; 59import static org.mockito.Mockito.mock; 60import static org.mockito.Mockito.reset; 61import static org.mockito.Mockito.spy; 62import static org.mockito.Mockito.times; 63import static org.mockito.Mockito.verify; 64import static org.mockito.Mockito.verifyNoMoreInteractions; 65import static org.mockito.Mockito.when; 66 67import android.annotation.SuppressLint; 68import android.os.Bundle; 69import android.support.test.InstrumentationRegistry; 70import android.support.test.filters.SdkSuppress; 71import android.support.test.filters.SmallTest; 72import android.support.test.runner.AndroidJUnit4; 73import android.support.text.emoji.EmojiCompat.Config; 74import android.support.text.emoji.util.Emoji.EmojiMapping; 75import android.support.text.emoji.util.TestString; 76import android.text.Editable; 77import android.text.Selection; 78import android.text.Spannable; 79import android.text.SpannableString; 80import android.text.SpannableStringBuilder; 81import android.text.SpannedString; 82import android.view.KeyEvent; 83import android.view.inputmethod.EditorInfo; 84import android.view.inputmethod.InputConnection; 85 86import org.junit.Before; 87import org.junit.Test; 88import org.junit.runner.RunWith; 89 90import java.util.Collections; 91import java.util.HashSet; 92import java.util.Set; 93 94@SmallTest 95@RunWith(AndroidJUnit4.class) 96public class EmojiCompatTest { 97 98 @Before 99 public void setup() { 100 EmojiCompat.reset(config()); 101 } 102 103 @Test(expected = IllegalStateException.class) 104 public void testGet_throwsException() { 105 EmojiCompat.reset((EmojiCompat) null); 106 EmojiCompat.get(); 107 } 108 109 @Test 110 public void testProcess_doesNothing_withNullCharSequence() { 111 assertNull(EmojiCompat.get().process(null)); 112 } 113 114 @Test 115 public void testProcess_returnsEmptySpanned_withEmptyString() { 116 final CharSequence charSequence = EmojiCompat.get().process(""); 117 assertNotNull(charSequence); 118 assertEquals(0, charSequence.length()); 119 assertThat(charSequence, not(hasEmoji())); 120 } 121 122 @SuppressLint("Range") 123 @Test(expected = IllegalArgumentException.class) 124 public void testProcess_withNegativeStartValue() { 125 EmojiCompat.get().process("a", -1, 1); 126 } 127 128 @SuppressLint("Range") 129 @Test(expected = IllegalArgumentException.class) 130 public void testProcess_withNegativeEndValue() { 131 EmojiCompat.get().process("a", 1, -1); 132 } 133 134 @Test(expected = IllegalArgumentException.class) 135 public void testProcess_withStartSmallerThanEndValue() { 136 EmojiCompat.get().process("aa", 1, 0); 137 } 138 139 @Test(expected = IllegalArgumentException.class) 140 public void testProcess_withStartGreaterThanLength() { 141 EmojiCompat.get().process("a", 2, 2); 142 } 143 144 @Test(expected = IllegalArgumentException.class) 145 public void testProcess_withEndGreaterThanLength() { 146 EmojiCompat.get().process("a", 0, 2); 147 } 148 149 @Test 150 public void testProcessWithStartEnd_withNoOpValues() { 151 final Spannable spannable = new SpannableString(new TestString('a') 152 .withPrefix().withSuffix().toString()); 153 // early return check 154 assertSame(spannable, EmojiCompat.get().process(spannable, 0, 0)); 155 assertSame(spannable, EmojiCompat.get().process(spannable, 1, 1)); 156 assertSame(spannable, EmojiCompat.get().process(spannable, spannable.length(), 157 spannable.length())); 158 } 159 160 @Test 161 public void testProcess_doesNotAddEmojiSpan() { 162 final String string = "abc"; 163 final CharSequence charSequence = EmojiCompat.get().process(string); 164 assertNotNull(charSequence); 165 assertEquals(string, charSequence.toString()); 166 assertThat(charSequence, not(hasEmoji())); 167 } 168 169 @Test 170 @SdkSuppress(maxSdkVersion = 18) 171 public void testProcess_returnsSameCharSequence_pre19() { 172 assertNull(EmojiCompat.get().process(null)); 173 174 CharSequence testString = "abc"; 175 assertSame(testString, EmojiCompat.get().process(testString)); 176 177 testString = new SpannableString("abc"); 178 assertSame(testString, EmojiCompat.get().process(testString)); 179 180 testString = new TestString(new int[]{CHAR_DEFAULT_EMOJI_STYLE}).toString(); 181 assertSame(testString, EmojiCompat.get().process(testString)); 182 } 183 184 @Test 185 @SdkSuppress(minSdkVersion = 19) 186 public void testProcess_addsSingleCodePointEmoji() { 187 assertCodePointMatch(EMOJI_SINGLE_CODEPOINT); 188 } 189 190 @Test 191 @SdkSuppress(minSdkVersion = 19) 192 public void testProcess_addsFlagEmoji() { 193 assertCodePointMatch(EMOJI_FLAG); 194 } 195 196 @Test 197 @SdkSuppress(minSdkVersion = 19) 198 public void testProcess_addsUnknownFlagEmoji() { 199 assertCodePointMatch(EMOJI_UNKNOWN_FLAG); 200 } 201 202 @Test 203 @SdkSuppress(minSdkVersion = 19) 204 public void testProcess_addsRegionalIndicatorSymbol() { 205 assertCodePointMatch(EMOJI_REGIONAL_SYMBOL); 206 } 207 208 @Test 209 @SdkSuppress(minSdkVersion = 19) 210 public void testProcess_addsKeyCapEmoji() { 211 assertCodePointMatch(EMOJI_DIGIT_KEYCAP); 212 } 213 214 @Test 215 @SdkSuppress(minSdkVersion = 19) 216 public void testProcess_doesNotAddEmojiForNumbers() { 217 assertCodePointDoesNotMatch(new int[] {CHAR_DIGIT}); 218 } 219 220 @Test 221 @SdkSuppress(minSdkVersion = 19) 222 public void testProcess_doesNotAddEmojiForNumbers_1() { 223 final TestString string = new TestString(EMOJI_SINGLE_CODEPOINT).append('1', 'f'); 224 CharSequence charSequence = EmojiCompat.get().process(string.toString()); 225 assertThat(charSequence, hasEmojiCount(1)); 226 } 227 228 @Test 229 @SdkSuppress(minSdkVersion = 19) 230 public void testProcess_addsVariantSelectorEmoji() { 231 assertCodePointMatch(EMOJI_DIGIT_ES); 232 } 233 234 @Test 235 @SdkSuppress(minSdkVersion = 19) 236 public void testProcess_doesNotAddVariantSelectorTextStyle() { 237 assertCodePointDoesNotMatch(new int[]{CHAR_DIGIT, CHAR_VS_TEXT}); 238 } 239 240 @Test 241 @SdkSuppress(minSdkVersion = 19) 242 public void testProcess_addsVariantSelectorAndKeyCapEmoji() { 243 assertCodePointMatch(EMOJI_DIGIT_ES_KEYCAP); 244 } 245 246 @Test 247 public void testProcess_doesNotAddEmoji_forVariantBaseWithoutSelector() { 248 assertCodePointDoesNotMatch(new int[]{CHAR_DIGIT}); 249 } 250 251 @Test 252 @SdkSuppress(minSdkVersion = 19) 253 public void testProcess_addsAsteriskKeyCapEmoji() { 254 assertCodePointMatch(EMOJI_ASTERISK_KEYCAP); 255 } 256 257 @Test 258 @SdkSuppress(minSdkVersion = 19) 259 public void testProcess_addsSkinModifierEmoji() { 260 assertCodePointMatch(EMOJI_SKIN_MODIFIER); 261 assertCodePointMatch(EMOJI_SKIN_MODIFIER_TYPE_ONE); 262 } 263 264 @Test 265 @SdkSuppress(minSdkVersion = 19) 266 public void testProcess_addsSkinModifierEmoji_withVariantSelector() { 267 assertCodePointMatch(EMOJI_SKIN_MODIFIER_WITH_VS); 268 } 269 270 @Test 271 @SdkSuppress(minSdkVersion = 19) 272 public void testProcess_addsSkinModifierEmoji_270c_withVariantSelector() { 273 // 0x270c is a Standardized Variant Base, Emoji Modifier Base and also Emoji 274 // therefore it is different than i.e. 0x1f3c3. The code actually failed for this test 275 // at first. 276 assertCodePointMatch(0xF0734, new int[]{0x270C, CHAR_VS_EMOJI, CHAR_FITZPATRICK}); 277 } 278 279 @Test 280 @SdkSuppress(minSdkVersion = 19) 281 public void testProcess_defaultStyleDoesNotAddSpan() { 282 assertCodePointDoesNotMatch(new int[]{CHAR_DEFAULT_TEXT_STYLE}); 283 assertCodePointMatch(DEFAULT_TEXT_STYLE); 284 } 285 286 @Test 287 @SdkSuppress(minSdkVersion = 19) 288 public void testProcess_defaultEmojiStyle_withTextStyleVs() { 289 assertCodePointMatch(EMOJI_SINGLE_CODEPOINT.id(), 290 new int[]{CHAR_DEFAULT_EMOJI_STYLE, CHAR_VS_EMOJI}); 291 assertCodePointDoesNotMatch(new int[]{CHAR_DEFAULT_EMOJI_STYLE, CHAR_VS_TEXT}); 292 } 293 294 @Test 295 @SdkSuppress(minSdkVersion = 19) 296 public void testProcess_genderEmoji() { 297 assertCodePointMatch(EMOJI_GENDER); 298 assertCodePointMatch(EMOJI_GENDER_WITHOUT_VS); 299 } 300 301 @Test 302 @SdkSuppress(minSdkVersion = 19) 303 public void testProcess_standardizedVariantEmojiExceptions() { 304 final int[][] exceptions = new int[][]{ 305 {0x2600, 0xF034D}, 306 {0x2601, 0xF0167}, 307 {0x260E, 0xF034E}, 308 {0x261D, 0xF0227}, 309 {0x263A, 0xF02A6}, 310 {0x2660, 0xF0350}, 311 {0x2663, 0xF033F}, 312 {0x2665, 0xF033B}, 313 {0x2666, 0xF033E}, 314 {0x270C, 0xF0079}, 315 {0x2744, 0xF0342}, 316 {0x2764, 0xF0362} 317 }; 318 319 for (int i = 0; i < exceptions.length; i++) { 320 final int[] codepoints = new int[]{exceptions[i][0]}; 321 assertCodePointMatch(exceptions[i][1], codepoints); 322 } 323 } 324 325 @Test 326 @SdkSuppress(minSdkVersion = 19) 327 public void testProcess_addsZwjEmoji() { 328 assertCodePointMatch(EMOJI_WITH_ZWJ); 329 } 330 331 @Test 332 @SdkSuppress(minSdkVersion = 19) 333 public void testProcess_doesNotAddEmojiForNumbersAfterZwjEmo() { 334 TestString string = new TestString(EMOJI_WITH_ZWJ).append(0x20, 0x2B, 0x31) 335 .withSuffix().withPrefix(); 336 CharSequence charSequence = EmojiCompat.get().process(string.toString()); 337 assertThat(charSequence, hasEmojiAt(EMOJI_WITH_ZWJ, string.emojiStartIndex(), 338 string.emojiEndIndex() - 3)); 339 assertThat(charSequence, hasEmojiCount(1)); 340 341 string = new TestString(EMOJI_WITH_ZWJ).withSuffix().withPrefix(); 342 charSequence = EmojiCompat.get().process(string.toString()); 343 assertThat(charSequence, hasEmojiCount(1)); 344 } 345 346 @Test 347 @SdkSuppress(minSdkVersion = 19) 348 public void testProcess_withAppend() { 349 final Editable editable = new SpannableStringBuilder(new TestString('a').withPrefix() 350 .withSuffix().toString()); 351 final int start = 1; 352 final int end = start + EMOJI_SINGLE_CODEPOINT.charCount(); 353 editable.insert(start, new TestString(EMOJI_SINGLE_CODEPOINT).toString()); 354 EmojiCompat.get().process(editable, start, end); 355 assertThat(editable, hasEmojiCount(1)); 356 assertThat(editable, hasEmojiAt(EMOJI_SINGLE_CODEPOINT, start, end)); 357 } 358 359 @Test 360 public void testProcess_doesNotCreateSpannable_ifNoEmoji() { 361 CharSequence processed = EmojiCompat.get().process("abc"); 362 assertNotNull(processed); 363 assertThat(processed, instanceOf(String.class)); 364 365 processed = EmojiCompat.get().process(new SpannedString("abc")); 366 assertNotNull(processed); 367 assertThat(processed, instanceOf(SpannedString.class)); 368 } 369 370 @Test 371 @SdkSuppress(minSdkVersion = 19) 372 public void testProcess_reprocess() { 373 final String string = new TestString(EMOJI_SINGLE_CODEPOINT) 374 .append(EMOJI_SINGLE_CODEPOINT) 375 .append(EMOJI_SINGLE_CODEPOINT) 376 .withPrefix().withSuffix().toString(); 377 378 Spannable processed = (Spannable) EmojiCompat.get().process(string); 379 assertThat(processed, hasEmojiCount(3)); 380 381 final EmojiSpan[] spans = processed.getSpans(0, processed.length(), EmojiSpan.class); 382 final Set<EmojiSpan> spanSet = new HashSet<>(); 383 Collections.addAll(spanSet, spans); 384 385 processed = (Spannable) EmojiCompat.get().process(processed); 386 assertThat(processed, hasEmojiCount(3)); 387 // new spans should be new instances 388 final EmojiSpan[] newSpans = processed.getSpans(0, processed.length(), EmojiSpan.class); 389 for (int i = 0; i < newSpans.length; i++) { 390 assertFalse(spanSet.contains(newSpans[i])); 391 } 392 } 393 394 @SuppressLint("Range") 395 @Test(expected = IllegalArgumentException.class) 396 public void testProcess_throwsException_withMaxEmojiSetToNegative() { 397 final String original = new TestString(EMOJI_SINGLE_CODEPOINT).toString(); 398 399 final CharSequence processed = EmojiCompat.get().process(original, 0, original.length(), 400 -1 /*maxEmojiCount*/); 401 402 assertThat(processed, not(hasEmoji())); 403 } 404 405 @Test 406 public void testProcess_withMaxEmojiSetToZero() { 407 final String original = new TestString(EMOJI_SINGLE_CODEPOINT).toString(); 408 409 final CharSequence processed = EmojiCompat.get().process(original, 0, original.length(), 410 0 /*maxEmojiCount*/); 411 412 assertThat(processed, not(hasEmoji())); 413 } 414 415 @Test 416 @SdkSuppress(minSdkVersion = 19) 417 public void testProcess_withMaxEmojiSetToOne() { 418 final String original = new TestString(EMOJI_SINGLE_CODEPOINT).toString(); 419 420 final CharSequence processed = EmojiCompat.get().process(original, 0, original.length(), 421 1 /*maxEmojiCount*/); 422 423 assertThat(processed, hasEmojiCount(1)); 424 assertThat(processed, hasEmoji(EMOJI_SINGLE_CODEPOINT)); 425 } 426 427 @Test 428 @SdkSuppress(minSdkVersion = 19) 429 public void testProcess_withMaxEmojiSetToLessThenExistingSpanCount() { 430 final String original = new TestString(EMOJI_SINGLE_CODEPOINT) 431 .append(EMOJI_SINGLE_CODEPOINT) 432 .append(EMOJI_SINGLE_CODEPOINT) 433 .toString(); 434 435 // add 2 spans 436 final CharSequence processed = EmojiCompat.get().process(original, 0, original.length(), 2); 437 438 assertThat(processed, hasEmojiCount(2)); 439 440 // use the Spannable with 2 spans, but use maxEmojiCount=1, start from the beginning of 441 // last (3rd) emoji 442 EmojiCompat.get().process(processed, original.length() - EMOJI_SINGLE_CODEPOINT.charCount(), 443 original.length(), 1 /*maxEmojiCount*/); 444 445 // expectation: there are still 2 emojis 446 assertThat(processed, hasEmojiCount(2)); 447 } 448 449 @Test 450 @SdkSuppress(minSdkVersion = 19) 451 public void testProcess_withMaxEmojiSet_withExistingEmojis() { 452 // test string with two emoji characters 453 final String original = new TestString(EMOJI_SINGLE_CODEPOINT) 454 .append(EMOJI_FLAG).toString(); 455 456 // process and add 1 EmojiSpan, maxEmojiCount=1 457 CharSequence processed = EmojiCompat.get().process(original, 0, original.length(), 458 1 /*maxEmojiCount*/); 459 460 // assert that there is a single emoji 461 assertThat(processed, hasEmojiCount(1)); 462 assertThat(processed, 463 hasEmojiAt(EMOJI_SINGLE_CODEPOINT, 0, EMOJI_SINGLE_CODEPOINT.charCount())); 464 465 // call process again with the charSequence that already has 1 span 466 processed = EmojiCompat.get().process(processed, EMOJI_SINGLE_CODEPOINT.charCount(), 467 processed.length(), 1 /*maxEmojiCount*/); 468 469 // assert that there is still a single emoji 470 assertThat(processed, hasEmojiCount(1)); 471 assertThat(processed, 472 hasEmojiAt(EMOJI_SINGLE_CODEPOINT, 0, EMOJI_SINGLE_CODEPOINT.charCount())); 473 474 // make the same call, this time with maxEmojiCount=2 475 processed = EmojiCompat.get().process(processed, EMOJI_SINGLE_CODEPOINT.charCount(), 476 processed.length(), 2 /*maxEmojiCount*/); 477 478 // assert that it contains 2 emojis 479 assertThat(processed, hasEmojiCount(2)); 480 assertThat(processed, 481 hasEmojiAt(EMOJI_SINGLE_CODEPOINT, 0, EMOJI_SINGLE_CODEPOINT.charCount())); 482 assertThat(processed, 483 hasEmojiAt(EMOJI_FLAG, EMOJI_SINGLE_CODEPOINT.charCount(), 484 original.length())); 485 } 486 487 @Test 488 @SdkSuppress(minSdkVersion = 19) 489 public void testProcess_withReplaceNonExistent_callsGlyphChecker() { 490 final Config config = TestConfigBuilder.config().setReplaceAll(true); 491 EmojiCompat.reset(config); 492 493 final EmojiProcessor.GlyphChecker glyphChecker = mock(EmojiProcessor.GlyphChecker.class); 494 when(glyphChecker.hasGlyph(any(CharSequence.class), anyInt(), anyInt())).thenReturn(true); 495 EmojiCompat.get().setGlyphChecker(glyphChecker); 496 497 final String original = new TestString(EMOJI_SINGLE_CODEPOINT).toString(); 498 499 CharSequence processed = EmojiCompat.get().process(original, 0, original.length(), 500 Integer.MAX_VALUE /*maxEmojiCount*/, EmojiCompat.REPLACE_STRATEGY_NON_EXISTENT); 501 502 // when function overrides config level replaceAll, a call to GlyphChecker is expected. 503 verify(glyphChecker, times(1)).hasGlyph(any(CharSequence.class), anyInt(), anyInt()); 504 505 // since replaceAll is false, there should be no EmojiSpans 506 assertThat(processed, not(hasEmoji())); 507 } 508 509 @Test 510 @SdkSuppress(minSdkVersion = 19) 511 public void testProcess_withReplaceDefault_doesNotCallGlyphChecker() { 512 final Config config = TestConfigBuilder.config().setReplaceAll(true); 513 EmojiCompat.reset(config); 514 515 final EmojiProcessor.GlyphChecker glyphChecker = mock(EmojiProcessor.GlyphChecker.class); 516 when(glyphChecker.hasGlyph(any(CharSequence.class), anyInt(), anyInt())).thenReturn(true); 517 EmojiCompat.get().setGlyphChecker(glyphChecker); 518 519 final String original = new TestString(EMOJI_SINGLE_CODEPOINT).toString(); 520 // call without replaceAll, config value (true) should be used 521 final CharSequence processed = EmojiCompat.get().process(original, 0, original.length(), 522 Integer.MAX_VALUE /*maxEmojiCount*/, EmojiCompat.REPLACE_STRATEGY_DEFAULT); 523 524 // replaceAll=true should not call hasGlyph 525 verify(glyphChecker, times(0)).hasGlyph(any(CharSequence.class), anyInt(), anyInt()); 526 527 assertThat(processed, hasEmojiCount(1)); 528 assertThat(processed, hasEmoji(EMOJI_SINGLE_CODEPOINT)); 529 } 530 531 @Test(expected = NullPointerException.class) 532 public void testHasEmojiGlyph_withNullCharSequence() { 533 EmojiCompat.get().hasEmojiGlyph(null); 534 } 535 536 @Test(expected = NullPointerException.class) 537 public void testHasEmojiGlyph_withMetadataVersion_withNullCharSequence() { 538 EmojiCompat.get().hasEmojiGlyph(null, Integer.MAX_VALUE); 539 } 540 541 @Test 542 @SdkSuppress(maxSdkVersion = 18) 543 public void testHasEmojiGlyph_pre19() { 544 String sequence = new TestString(new int[]{CHAR_DEFAULT_EMOJI_STYLE}).toString(); 545 assertFalse(EmojiCompat.get().hasEmojiGlyph(sequence)); 546 } 547 548 @Test 549 @SdkSuppress(maxSdkVersion = 18) 550 public void testHasEmojiGlyph_withMetaVersion_pre19() { 551 String sequence = new TestString(new int[]{CHAR_DEFAULT_EMOJI_STYLE}).toString(); 552 assertFalse(EmojiCompat.get().hasEmojiGlyph(sequence, Integer.MAX_VALUE)); 553 } 554 555 @Test 556 @SdkSuppress(minSdkVersion = 19) 557 public void testHasEmojiGlyph_returnsTrueForExistingEmoji() { 558 final String sequence = new TestString(EMOJI_FLAG).toString(); 559 assertTrue(EmojiCompat.get().hasEmojiGlyph(sequence)); 560 } 561 562 @Test 563 public void testHasGlyph_returnsFalseForNonExistentEmoji() { 564 final String sequence = new TestString(EMOJI_FLAG).append(0x1111).toString(); 565 assertFalse(EmojiCompat.get().hasEmojiGlyph(sequence)); 566 } 567 568 @Test 569 @SdkSuppress(minSdkVersion = 19) 570 public void testHashEmojiGlyph_withDefaultEmojiStyles() { 571 String sequence = new TestString(new int[]{CHAR_DEFAULT_EMOJI_STYLE}).toString(); 572 assertTrue(EmojiCompat.get().hasEmojiGlyph(sequence)); 573 574 sequence = new TestString(new int[]{CHAR_DEFAULT_EMOJI_STYLE, CHAR_VS_EMOJI}).toString(); 575 assertTrue(EmojiCompat.get().hasEmojiGlyph(sequence)); 576 577 sequence = new TestString(new int[]{CHAR_DEFAULT_EMOJI_STYLE, CHAR_VS_TEXT}).toString(); 578 assertFalse(EmojiCompat.get().hasEmojiGlyph(sequence)); 579 } 580 581 @Test 582 @SdkSuppress(minSdkVersion = 19) 583 public void testHashEmojiGlyph_withMetadataVersion() { 584 final String sequence = new TestString(EMOJI_SINGLE_CODEPOINT).toString(); 585 assertFalse(EmojiCompat.get().hasEmojiGlyph(sequence, 0)); 586 assertTrue(EmojiCompat.get().hasEmojiGlyph(sequence, Integer.MAX_VALUE)); 587 } 588 589 @Test 590 @SdkSuppress(maxSdkVersion = 18) 591 public void testGetLoadState_returnsSuccess_pre19() { 592 assertEquals(EmojiCompat.get().getLoadState(), EmojiCompat.LOAD_STATE_SUCCEEDED); 593 } 594 595 @Test 596 @SdkSuppress(minSdkVersion = 19) 597 public void testGetLoadState_returnsSuccessIfLoadSuccess() throws InterruptedException { 598 final WaitingDataLoader metadataLoader = new WaitingDataLoader(true /*success*/); 599 final Config config = new TestConfig(metadataLoader); 600 EmojiCompat.reset(config); 601 602 assertEquals(EmojiCompat.get().getLoadState(), EmojiCompat.LOAD_STATE_LOADING); 603 604 metadataLoader.getLoaderLatch().countDown(); 605 metadataLoader.getTestLatch().await(); 606 InstrumentationRegistry.getInstrumentation().waitForIdleSync(); 607 608 assertEquals(EmojiCompat.get().getLoadState(), EmojiCompat.LOAD_STATE_SUCCEEDED); 609 } 610 611 @Test 612 @SdkSuppress(minSdkVersion = 19) 613 public void testGetLoadState_returnsFailIfLoadFail() throws InterruptedException { 614 final WaitingDataLoader metadataLoader = new WaitingDataLoader(false/*fail*/); 615 final Config config = new TestConfig(metadataLoader); 616 EmojiCompat.reset(config); 617 618 assertEquals(EmojiCompat.get().getLoadState(), EmojiCompat.LOAD_STATE_LOADING); 619 620 metadataLoader.getLoaderLatch().countDown(); 621 metadataLoader.getTestLatch().await(); 622 InstrumentationRegistry.getInstrumentation().waitForIdleSync(); 623 624 assertEquals(EmojiCompat.get().getLoadState(), EmojiCompat.LOAD_STATE_FAILED); 625 } 626 627 @Test 628 public void testUpdateEditorInfoAttrs_doesNotSetKeyIfNotInitialized() { 629 final EditorInfo editorInfo = new EditorInfo(); 630 editorInfo.extras = new Bundle(); 631 632 final WaitingDataLoader metadataLoader = new WaitingDataLoader(); 633 final Config config = new TestConfig(metadataLoader); 634 EmojiCompat.reset(config); 635 636 EmojiCompat.get().updateEditorInfoAttrs(editorInfo); 637 638 final Bundle extras = editorInfo.extras; 639 assertFalse(extras.containsKey(EmojiCompat.EDITOR_INFO_METAVERSION_KEY)); 640 assertFalse(extras.containsKey(EmojiCompat.EDITOR_INFO_REPLACE_ALL_KEY)); 641 642 metadataLoader.getLoaderLatch().countDown(); 643 } 644 645 @Test 646 @SdkSuppress(minSdkVersion = 19) 647 public void testUpdateEditorInfoAttrs_setsKeysIfInitialized() { 648 final EditorInfo editorInfo = new EditorInfo(); 649 editorInfo.extras = new Bundle(); 650 Config config = new TestConfig().setReplaceAll(false); 651 EmojiCompat.reset(config); 652 EmojiCompat.get().updateEditorInfoAttrs(editorInfo); 653 654 final Bundle extras = editorInfo.extras; 655 assertTrue(extras.containsKey(EmojiCompat.EDITOR_INFO_METAVERSION_KEY)); 656 assertTrue(extras.getInt(EmojiCompat.EDITOR_INFO_METAVERSION_KEY) > 0); 657 assertTrue(extras.containsKey(EmojiCompat.EDITOR_INFO_REPLACE_ALL_KEY)); 658 assertFalse(extras.getBoolean(EmojiCompat.EDITOR_INFO_REPLACE_ALL_KEY)); 659 660 config = new TestConfig().setReplaceAll(true); 661 EmojiCompat.reset(config); 662 EmojiCompat.get().updateEditorInfoAttrs(editorInfo); 663 664 assertTrue(extras.containsKey(EmojiCompat.EDITOR_INFO_REPLACE_ALL_KEY)); 665 assertTrue(extras.getBoolean(EmojiCompat.EDITOR_INFO_REPLACE_ALL_KEY)); 666 } 667 668 @Test 669 @SdkSuppress(maxSdkVersion = 18) 670 public void testHandleDeleteSurroundingText_pre19() { 671 final TestString testString = new TestString(EMOJI_SINGLE_CODEPOINT); 672 final InputConnection inputConnection = mock(InputConnection.class); 673 final Editable editable = spy(new SpannableStringBuilder(testString.toString())); 674 675 Selection.setSelection(editable, testString.emojiEndIndex()); 676 677 reset(editable); 678 reset(inputConnection); 679 verifyNoMoreInteractions(editable); 680 verifyNoMoreInteractions(inputConnection); 681 682 // try backwards delete 1 character 683 assertFalse( 684 EmojiCompat.handleDeleteSurroundingText(inputConnection, editable, 1, 0, false)); 685 } 686 687 @Test 688 @SdkSuppress(maxSdkVersion = 18) 689 public void testOnKeyDown_pre19() { 690 final TestString testString = new TestString(EMOJI_SINGLE_CODEPOINT); 691 final Editable editable = spy(new SpannableStringBuilder(testString.toString())); 692 Selection.setSelection(editable, testString.emojiEndIndex()); 693 final KeyEvent event = del(); 694 695 reset(editable); 696 verifyNoMoreInteractions(editable); 697 698 assertFalse(EmojiCompat.handleOnKeyDown(editable, event.getKeyCode(), event)); 699 } 700 701 private void assertCodePointMatch(EmojiMapping emoji) { 702 assertCodePointMatch(emoji.id(), emoji.codepoints()); 703 } 704 705 private void assertCodePointMatch(int id, int[] codepoints) { 706 TestString string = new TestString(codepoints); 707 CharSequence charSequence = EmojiCompat.get().process(string.toString()); 708 assertThat(charSequence, hasEmojiAt(id, string.emojiStartIndex(), string.emojiEndIndex())); 709 710 // case where Emoji is in the middle of string 711 string = new TestString(codepoints).withPrefix().withSuffix(); 712 charSequence = EmojiCompat.get().process(string.toString()); 713 assertThat(charSequence, hasEmojiAt(id, string.emojiStartIndex(), string.emojiEndIndex())); 714 715 // case where Emoji is at the end of string 716 string = new TestString(codepoints).withSuffix(); 717 charSequence = EmojiCompat.get().process(string.toString()); 718 assertThat(charSequence, hasEmojiAt(id, string.emojiStartIndex(), string.emojiEndIndex())); 719 } 720 721 private void assertCodePointDoesNotMatch(int[] codepoints) { 722 TestString string = new TestString(codepoints); 723 CharSequence charSequence = EmojiCompat.get().process(string.toString()); 724 assertThat(charSequence, not(hasEmoji())); 725 726 string = new TestString(codepoints).withSuffix().withPrefix(); 727 charSequence = EmojiCompat.get().process(string.toString()); 728 assertThat(charSequence, not(hasEmoji())); 729 730 string = new TestString(codepoints).withPrefix(); 731 charSequence = EmojiCompat.get().process(string.toString()); 732 assertThat(charSequence, not(hasEmoji())); 733 } 734} 735