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