1/*
2 * Copyright 2018 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 androidx.emoji.text;
17
18import static androidx.emoji.util.EmojiMatcher.hasEmojiAt;
19import static androidx.emoji.util.EmojiMatcher.hasEmojiCount;
20
21import static org.junit.Assert.assertThat;
22import static org.junit.Assert.assertTrue;
23
24import android.content.Context;
25import android.graphics.Paint;
26import android.support.test.InstrumentationRegistry;
27import android.support.test.filters.SdkSuppress;
28import android.support.test.filters.SmallTest;
29import android.text.Spanned;
30
31import androidx.core.graphics.PaintCompat;
32import androidx.emoji.util.TestString;
33
34import org.junit.BeforeClass;
35import org.junit.Test;
36import org.junit.runner.RunWith;
37import org.junit.runners.Parameterized;
38
39import java.io.BufferedReader;
40import java.io.IOException;
41import java.io.InputStream;
42import java.io.InputStreamReader;
43import java.util.ArrayList;
44import java.util.Collection;
45
46/**
47 * Reads raw/allemojis.txt which includes all the emojis known to human kind and tests that
48 * EmojiCompat creates EmojiSpans for each one of them.
49 */
50@SmallTest
51@RunWith(Parameterized.class)
52@SdkSuppress(minSdkVersion = 19)
53public class AllEmojisTest {
54
55    /**
56     * String representation for a single emoji
57     */
58    private String mString;
59
60    /**
61     * Codepoints of emoji for better assert error message.
62     */
63    private String mCodepoints;
64
65    /**
66     * Paint object used to check if Typeface can render the given emoji.
67     */
68    private Paint mPaint;
69
70    @BeforeClass
71    public static void setup() {
72        EmojiCompat.reset(TestConfigBuilder.config());
73    }
74
75    @Parameterized.Parameters
76    public static Collection<Object[]> data() throws IOException {
77        final Context context = InstrumentationRegistry.getTargetContext();
78        final InputStream inputStream = context.getAssets().open("emojis.txt");
79        try {
80            final BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
81            final Collection<Object[]> data = new ArrayList<>();
82            final StringBuilder stringBuilder = new StringBuilder();
83            final StringBuilder codePointsBuilder = new StringBuilder();
84
85            String s;
86            while ((s = reader.readLine()) != null) {
87                s = s.trim();
88                // pass comments
89                if (s.isEmpty() || s.startsWith("#")) continue;
90
91                stringBuilder.setLength(0);
92                codePointsBuilder.setLength(0);
93
94                // emoji codepoints are space separated: i.e. 0x1f1e6 0x1f1e8
95                final String[] split = s.split(" ");
96
97                for (int index = 0; index < split.length; index++) {
98                    final String part = split[index].trim();
99                    codePointsBuilder.append(part);
100                    codePointsBuilder.append(",");
101                    stringBuilder.append(Character.toChars(Integer.parseInt(part, 16)));
102                }
103                data.add(new Object[]{stringBuilder.toString(), codePointsBuilder.toString()});
104            }
105
106            return data;
107        } finally {
108            inputStream.close();
109        }
110
111    }
112
113    public AllEmojisTest(String string, String codepoints) {
114        mString = string;
115        mCodepoints = codepoints;
116        mPaint = new Paint();
117    }
118
119    @Test
120    public void testEmoji() {
121        assertTrue("EmojiCompat should have emoji: " + mCodepoints,
122                EmojiCompat.get().hasEmojiGlyph(mString));
123        assertEmojiCompatAddsEmoji(mString);
124        assertSpanCanRenderEmoji(mString);
125    }
126
127    private void assertSpanCanRenderEmoji(final String str) {
128        final Spanned spanned = (Spanned) EmojiCompat.get().process(new TestString(str).toString());
129        final EmojiSpan[] spans = spanned.getSpans(0, spanned.length(), EmojiSpan.class);
130        final EmojiMetadata metadata = spans[0].getMetadata();
131        mPaint.setTypeface(metadata.getTypeface());
132
133        final String codepoint = String.valueOf(Character.toChars(metadata.getId()));
134        assertTrue(metadata.toString() + " should be rendered",
135                PaintCompat.hasGlyph(mPaint, codepoint));
136    }
137
138    private void assertEmojiCompatAddsEmoji(final String str) {
139        TestString string = new TestString(str);
140        CharSequence sequence = EmojiCompat.get().process(string.toString());
141        assertThat(sequence, hasEmojiCount(1));
142        assertThat(sequence, hasEmojiAt(string.emojiStartIndex(), string.emojiEndIndex()));
143
144        // case where Emoji is in the middle of string
145        string = new TestString(str).withPrefix().withSuffix();
146        sequence = EmojiCompat.get().process(string.toString());
147        assertThat(sequence, hasEmojiCount(1));
148        assertThat(sequence, hasEmojiAt(string.emojiStartIndex(), string.emojiEndIndex()));
149
150        // case where Emoji is at the end of string
151        string = new TestString(str).withSuffix();
152        sequence = EmojiCompat.get().process(string.toString());
153        assertThat(sequence, hasEmojiCount(1));
154        assertThat(sequence, hasEmojiAt(string.emojiStartIndex(), string.emojiEndIndex()));
155    }
156
157}
158