blob: b7869d03c8989e6aef20fab59a60c1168962491d [file] [log] [blame]
Abodunrinwa Toki65b25272017-04-13 17:25:59 +01001/*
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 */
16
17package android.view.textclassifier;
18
Richard Ledleydb18a572017-11-30 17:33:51 +000019import static org.hamcrest.CoreMatchers.not;
Abodunrinwa Toki65b25272017-04-13 17:25:59 +010020import static org.junit.Assert.assertEquals;
21import static org.junit.Assert.assertThat;
Jan Althaus108aad32018-01-30 15:26:55 +010022import static org.junit.Assert.assertTrue;
Abodunrinwa Toki65b25272017-04-13 17:25:59 +010023import static org.mockito.Mockito.mock;
24
25import android.os.LocaleList;
26import android.support.test.InstrumentationRegistry;
27import android.support.test.filters.SmallTest;
28import android.support.test.runner.AndroidJUnit4;
Abodunrinwa Toki65b25272017-04-13 17:25:59 +010029
30import org.hamcrest.BaseMatcher;
31import org.hamcrest.Description;
32import org.hamcrest.Matcher;
33import org.junit.Before;
34import org.junit.Test;
35import org.junit.runner.RunWith;
36
Jan Althaus108aad32018-01-30 15:26:55 +010037import java.util.Arrays;
Richard Ledley1fc998b2018-02-16 15:45:06 +000038import java.util.Collections;
Jan Althaus108aad32018-01-30 15:26:55 +010039
Abodunrinwa Toki65b25272017-04-13 17:25:59 +010040@SmallTest
41@RunWith(AndroidJUnit4.class)
42public class TextClassificationManagerTest {
43
44 private static final LocaleList LOCALES = LocaleList.forLanguageTags("en");
Abodunrinwa Tokib4162972017-05-05 18:07:17 +010045 private static final String NO_TYPE = null;
Abodunrinwa Toki65b25272017-04-13 17:25:59 +010046
47 private TextClassificationManager mTcm;
48 private TextClassifier mClassifier;
Abodunrinwa Toki4d232d62017-11-23 12:22:45 +000049 private TextSelection.Options mSelectionOptions;
50 private TextClassification.Options mClassificationOptions;
51 private TextLinks.Options mLinksOptions;
Abodunrinwa Toki65b25272017-04-13 17:25:59 +010052
53 @Before
54 public void setup() {
55 mTcm = InstrumentationRegistry.getTargetContext()
56 .getSystemService(TextClassificationManager.class);
57 mTcm.setTextClassifier(null);
58 mClassifier = mTcm.getTextClassifier();
Abodunrinwa Toki4d232d62017-11-23 12:22:45 +000059 mSelectionOptions = new TextSelection.Options().setDefaultLocales(LOCALES);
60 mClassificationOptions = new TextClassification.Options().setDefaultLocales(LOCALES);
61 mLinksOptions = new TextLinks.Options().setDefaultLocales(LOCALES);
Abodunrinwa Toki65b25272017-04-13 17:25:59 +010062 }
63
64 @Test
65 public void testSmartSelection() {
66 if (isTextClassifierDisabled()) return;
67
68 String text = "Contact me at droid@android.com";
69 String selected = "droid";
70 String suggested = "droid@android.com";
71 int startIndex = text.indexOf(selected);
72 int endIndex = startIndex + selected.length();
73 int smartStartIndex = text.indexOf(suggested);
74 int smartEndIndex = smartStartIndex + suggested.length();
75
Abodunrinwa Toki4d232d62017-11-23 12:22:45 +000076 TextSelection selection = mClassifier.suggestSelection(
77 text, startIndex, endIndex, mSelectionOptions);
78 assertThat(selection,
Abodunrinwa Tokia2df6e52017-04-13 09:56:52 +010079 isTextSelection(smartStartIndex, smartEndIndex, TextClassifier.TYPE_EMAIL));
80 }
81
82 @Test
Abodunrinwa Toki65b25272017-04-13 17:25:59 +010083 public void testSmartSelection_url() {
84 if (isTextClassifierDisabled()) return;
85
86 String text = "Visit http://www.android.com for more information";
87 String selected = "http";
88 String suggested = "http://www.android.com";
89 int startIndex = text.indexOf(selected);
90 int endIndex = startIndex + selected.length();
91 int smartStartIndex = text.indexOf(suggested);
92 int smartEndIndex = smartStartIndex + suggested.length();
93
Abodunrinwa Toki4d232d62017-11-23 12:22:45 +000094 TextSelection selection = mClassifier.suggestSelection(
95 text, startIndex, endIndex, mSelectionOptions);
96 assertThat(selection,
Abodunrinwa Toki65b25272017-04-13 17:25:59 +010097 isTextSelection(smartStartIndex, smartEndIndex, TextClassifier.TYPE_URL));
98 }
99
100 @Test
Abodunrinwa Tokib4162972017-05-05 18:07:17 +0100101 public void testSmartSelection_withEmoji() {
102 if (isTextClassifierDisabled()) return;
103
104 String text = "\uD83D\uDE02 Hello.";
105 String selected = "Hello";
106 int startIndex = text.indexOf(selected);
107 int endIndex = startIndex + selected.length();
108
Abodunrinwa Toki4d232d62017-11-23 12:22:45 +0000109 TextSelection selection = mClassifier.suggestSelection(
110 text, startIndex, endIndex, mSelectionOptions);
111 assertThat(selection,
Abodunrinwa Tokib4162972017-05-05 18:07:17 +0100112 isTextSelection(startIndex, endIndex, NO_TYPE));
113 }
114
115 @Test
Abodunrinwa Tokie0b57892017-04-28 19:59:57 +0100116 public void testClassifyText() {
Abodunrinwa Toki65b25272017-04-13 17:25:59 +0100117 if (isTextClassifierDisabled()) return;
118
119 String text = "Contact me at droid@android.com";
120 String classifiedText = "droid@android.com";
121 int startIndex = text.indexOf(classifiedText);
122 int endIndex = startIndex + classifiedText.length();
Abodunrinwa Toki4d232d62017-11-23 12:22:45 +0000123
124 TextClassification classification = mClassifier.classifyText(
125 text, startIndex, endIndex, mClassificationOptions);
126 assertThat(classification,
Abodunrinwa Toki86ef9822017-05-11 20:05:50 +0100127 isTextClassification(
128 classifiedText,
129 TextClassifier.TYPE_EMAIL,
130 "mailto:" + classifiedText));
Abodunrinwa Toki65b25272017-04-13 17:25:59 +0100131 }
132
133 @Test
Abodunrinwa Tokie0b57892017-04-28 19:59:57 +0100134 public void testTextClassifyText_url() {
Abodunrinwa Toki65b25272017-04-13 17:25:59 +0100135 if (isTextClassifierDisabled()) return;
136
Abodunrinwa Toki86ef9822017-05-11 20:05:50 +0100137 String text = "Visit www.android.com for more information";
Abodunrinwa Toki70d41cd2017-05-02 21:43:41 +0100138 String classifiedText = "www.android.com";
Abodunrinwa Toki65b25272017-04-13 17:25:59 +0100139 int startIndex = text.indexOf(classifiedText);
140 int endIndex = startIndex + classifiedText.length();
Abodunrinwa Toki4d232d62017-11-23 12:22:45 +0000141
142 TextClassification classification = mClassifier.classifyText(
143 text, startIndex, endIndex, mClassificationOptions);
144 assertThat(classification,
Abodunrinwa Toki86ef9822017-05-11 20:05:50 +0100145 isTextClassification(
146 classifiedText,
147 TextClassifier.TYPE_URL,
148 "http://" + classifiedText));
149 }
150
151 @Test
Jan Althauseaff57e2018-02-12 12:47:27 +0100152 public void testTextClassifyText_address() {
153 if (isTextClassifierDisabled()) return;
154
155 String text = "Brandschenkestrasse 110, Zürich, Switzerland";
156 TextClassification classification = mClassifier.classifyText(
157 text, 0, text.length(), mClassificationOptions);
158 assertThat(classification,
159 isTextClassification(
160 text,
161 TextClassifier.TYPE_ADDRESS,
162 "geo:0,0?q=Brandschenkestrasse+110%2C+Z%C3%BCrich%2C+Switzerland"));
163 }
164
165 @Test
Abodunrinwa Toki86ef9822017-05-11 20:05:50 +0100166 public void testTextClassifyText_url_inCaps() {
167 if (isTextClassifierDisabled()) return;
168
169 String text = "Visit HTTP://ANDROID.COM for more information";
170 String classifiedText = "HTTP://ANDROID.COM";
171 int startIndex = text.indexOf(classifiedText);
172 int endIndex = startIndex + classifiedText.length();
Abodunrinwa Toki4d232d62017-11-23 12:22:45 +0000173
174 TextClassification classification = mClassifier.classifyText(
175 text, startIndex, endIndex, mClassificationOptions);
176 assertThat(classification,
Abodunrinwa Toki86ef9822017-05-11 20:05:50 +0100177 isTextClassification(
178 classifiedText,
179 TextClassifier.TYPE_URL,
180 "http://ANDROID.COM"));
Abodunrinwa Toki65b25272017-04-13 17:25:59 +0100181 }
182
183 @Test
Richard Ledleydb18a572017-11-30 17:33:51 +0000184 public void testGenerateLinks_phone() {
Richard Ledley68d94522017-10-05 10:52:19 +0100185 if (isTextClassifierDisabled()) return;
Richard Ledleydb18a572017-11-30 17:33:51 +0000186 String text = "The number is +12122537077. See you tonight!";
187 assertThat(mClassifier.generateLinks(text, null),
188 isTextLinksContaining(text, "+12122537077", TextClassifier.TYPE_PHONE));
189 }
Richard Ledley68d94522017-10-05 10:52:19 +0100190
Richard Ledleydb18a572017-11-30 17:33:51 +0000191 @Test
192 public void testGenerateLinks_exclude() {
193 if (isTextClassifierDisabled()) return;
194 String text = "The number is +12122537077. See you tonight!";
195 assertThat(mClassifier.generateLinks(text, mLinksOptions.setEntityConfig(
Richard Ledley1fc998b2018-02-16 15:45:06 +0000196 TextClassifier.EntityConfig.create(Collections.EMPTY_LIST,
197 Collections.EMPTY_LIST, Arrays.asList(TextClassifier.TYPE_PHONE)))),
Richard Ledleydb18a572017-11-30 17:33:51 +0000198 not(isTextLinksContaining(text, "+12122537077", TextClassifier.TYPE_PHONE)));
199 }
Richard Ledley68d94522017-10-05 10:52:19 +0100200
Richard Ledleydb18a572017-11-30 17:33:51 +0000201 @Test
Richard Ledley1fc998b2018-02-16 15:45:06 +0000202 public void testGenerateLinks_explicit_address() {
Richard Ledleydb18a572017-11-30 17:33:51 +0000203 if (isTextClassifierDisabled()) return;
Richard Ledley1fc998b2018-02-16 15:45:06 +0000204 String text = "The address is 1600 Amphitheater Parkway, Mountain View, CA. See you!";
205 assertThat(mClassifier.generateLinks(text, mLinksOptions.setEntityConfig(
206 TextClassifier.EntityConfig.createWithEntityList(
207 Arrays.asList(TextClassifier.TYPE_ADDRESS)))),
208 isTextLinksContaining(text, "1600 Amphitheater Parkway, Mountain View, CA",
209 TextClassifier.TYPE_ADDRESS));
210 }
Jan Althaus108aad32018-01-30 15:26:55 +0100211
Richard Ledley1fc998b2018-02-16 15:45:06 +0000212 @Test
213 public void testGenerateLinks_exclude_override() {
214 if (isTextClassifierDisabled()) return;
Richard Ledleydb18a572017-11-30 17:33:51 +0000215 String text = "The number is +12122537077. See you tonight!";
216 assertThat(mClassifier.generateLinks(text, mLinksOptions.setEntityConfig(
Richard Ledley1fc998b2018-02-16 15:45:06 +0000217 TextClassifier.EntityConfig.create(Collections.EMPTY_LIST,
218 Arrays.asList(TextClassifier.TYPE_PHONE),
219 Arrays.asList(TextClassifier.TYPE_PHONE)))),
Richard Ledleydb18a572017-11-30 17:33:51 +0000220 not(isTextLinksContaining(text, "+12122537077", TextClassifier.TYPE_PHONE)));
221 }
Richard Ledley68d94522017-10-05 10:52:19 +0100222
Richard Ledleydb18a572017-11-30 17:33:51 +0000223 @Test
Jan Althaus108aad32018-01-30 15:26:55 +0100224 public void testGenerateLinks_maxLength() {
225 if (isTextClassifierDisabled()) return;
226 char[] manySpaces = new char[mClassifier.getMaxGenerateLinksTextLength()];
227 Arrays.fill(manySpaces, ' ');
228 TextLinks links = mClassifier.generateLinks(new String(manySpaces), null);
229 assertTrue(links.getLinks().isEmpty());
230 }
231
232 @Test(expected = IllegalArgumentException.class)
233 public void testGenerateLinks_tooLong() {
234 if (isTextClassifierDisabled()) {
235 throw new IllegalArgumentException("pass if disabled");
236 }
237 char[] manySpaces = new char[mClassifier.getMaxGenerateLinksTextLength() + 1];
238 Arrays.fill(manySpaces, ' ');
239 mClassifier.generateLinks(new String(manySpaces), null);
240 }
241
242 @Test
Abodunrinwa Toki65b25272017-04-13 17:25:59 +0100243 public void testSetTextClassifier() {
244 TextClassifier classifier = mock(TextClassifier.class);
245 mTcm.setTextClassifier(classifier);
246 assertEquals(classifier, mTcm.getTextClassifier());
247 }
248
249 private boolean isTextClassifierDisabled() {
250 return mClassifier == TextClassifier.NO_OP;
251 }
252
253 private static Matcher<TextSelection> isTextSelection(
254 final int startIndex, final int endIndex, final String type) {
255 return new BaseMatcher<TextSelection>() {
256 @Override
257 public boolean matches(Object o) {
258 if (o instanceof TextSelection) {
259 TextSelection selection = (TextSelection) o;
260 return startIndex == selection.getSelectionStartIndex()
261 && endIndex == selection.getSelectionEndIndex()
Abodunrinwa Tokib4162972017-05-05 18:07:17 +0100262 && typeMatches(selection, type);
Abodunrinwa Toki65b25272017-04-13 17:25:59 +0100263 }
264 return false;
265 }
266
Abodunrinwa Tokib4162972017-05-05 18:07:17 +0100267 private boolean typeMatches(TextSelection selection, String type) {
268 return type == null
269 || (selection.getEntityCount() > 0
270 && type.trim().equalsIgnoreCase(selection.getEntity(0)));
271 }
272
Abodunrinwa Toki65b25272017-04-13 17:25:59 +0100273 @Override
274 public void describeTo(Description description) {
275 description.appendValue(
276 String.format("%d, %d, %s", startIndex, endIndex, type));
277 }
278 };
279 }
280
Richard Ledleydb18a572017-11-30 17:33:51 +0000281 private static Matcher<TextLinks> isTextLinksContaining(
282 final String text, final String substring, final String type) {
283 return new BaseMatcher<TextLinks>() {
284
285 @Override
286 public void describeTo(Description description) {
287 description.appendText("text=").appendValue(text)
288 .appendText(", substring=").appendValue(substring)
289 .appendText(", type=").appendValue(type);
290 }
291
292 @Override
293 public boolean matches(Object o) {
294 if (o instanceof TextLinks) {
295 for (TextLinks.TextLink link : ((TextLinks) o).getLinks()) {
296 if (text.subSequence(link.getStart(), link.getEnd()).equals(substring)) {
297 return type.equals(link.getEntity(0));
298 }
299 }
300 }
301 return false;
302 }
303 };
304 }
305
Abodunrinwa Tokie0b57892017-04-28 19:59:57 +0100306 private static Matcher<TextClassification> isTextClassification(
Abodunrinwa Toki86ef9822017-05-11 20:05:50 +0100307 final String text, final String type, final String intentUri) {
Abodunrinwa Tokie0b57892017-04-28 19:59:57 +0100308 return new BaseMatcher<TextClassification>() {
Abodunrinwa Toki65b25272017-04-13 17:25:59 +0100309 @Override
310 public boolean matches(Object o) {
Abodunrinwa Tokie0b57892017-04-28 19:59:57 +0100311 if (o instanceof TextClassification) {
312 TextClassification result = (TextClassification) o;
Abodunrinwa Toki70d41cd2017-05-02 21:43:41 +0100313 final boolean typeRequirementSatisfied;
Abodunrinwa Toki86ef9822017-05-11 20:05:50 +0100314 String scheme;
Abodunrinwa Toki70d41cd2017-05-02 21:43:41 +0100315 switch (type) {
Abodunrinwa Toki86ef9822017-05-11 20:05:50 +0100316 case TextClassifier.TYPE_EMAIL:
317 scheme = result.getIntent().getData().getScheme();
318 typeRequirementSatisfied = "mailto".equals(scheme);
319 break;
Abodunrinwa Toki70d41cd2017-05-02 21:43:41 +0100320 case TextClassifier.TYPE_URL:
Abodunrinwa Toki86ef9822017-05-11 20:05:50 +0100321 scheme = result.getIntent().getData().getScheme();
322 typeRequirementSatisfied = "http".equals(scheme)
323 || "https".equals(scheme);
Abodunrinwa Toki70d41cd2017-05-02 21:43:41 +0100324 break;
Jan Althauseaff57e2018-02-12 12:47:27 +0100325 case TextClassifier.TYPE_ADDRESS:
326 scheme = result.getIntent().getData().getScheme();
327 typeRequirementSatisfied = "geo".equals(scheme);
328 break;
Abodunrinwa Toki70d41cd2017-05-02 21:43:41 +0100329 default:
330 typeRequirementSatisfied = true;
331 }
332
333 return typeRequirementSatisfied
334 && text.equals(result.getText())
Abodunrinwa Toki65b25272017-04-13 17:25:59 +0100335 && result.getEntityCount() > 0
Abodunrinwa Toki86ef9822017-05-11 20:05:50 +0100336 && type.equals(result.getEntity(0))
337 && intentUri.equals(result.getIntent().getDataString());
Abodunrinwa Toki65b25272017-04-13 17:25:59 +0100338 // TODO: Include other properties.
339 }
340 return false;
341 }
342
343 @Override
344 public void describeTo(Description description) {
345 description.appendText("text=").appendValue(text)
Abodunrinwa Toki86ef9822017-05-11 20:05:50 +0100346 .appendText(", type=").appendValue(type)
347 .appendText(", intent.data=").appendValue(intentUri);
Abodunrinwa Toki65b25272017-04-13 17:25:59 +0100348 }
349 };
350 }
Abodunrinwa Toki65b25272017-04-13 17:25:59 +0100351}