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 */ 16 17 package android.view.textclassifier; 18 19 import static org.junit.Assert.assertEquals; 20 import static org.junit.Assert.assertThat; 21 import static org.mockito.Mockito.mock; 22 23 import android.os.LocaleList; 24 import android.support.test.InstrumentationRegistry; 25 import android.support.test.filters.SmallTest; 26 import android.support.test.runner.AndroidJUnit4; 27 28 import org.hamcrest.BaseMatcher; 29 import org.hamcrest.Description; 30 import org.hamcrest.Matcher; 31 import org.junit.Before; 32 import org.junit.Test; 33 import org.junit.runner.RunWith; 34 35 @SmallTest 36 @RunWith(AndroidJUnit4.class) 37 public class TextClassificationManagerTest { 38 39 private static final LocaleList LOCALES = LocaleList.forLanguageTags("en"); 40 private static final String NO_TYPE = null; 41 42 private TextClassificationManager mTcm; 43 private TextClassifier mClassifier; 44 45 @Before 46 public void setup() { 47 mTcm = InstrumentationRegistry.getTargetContext() 48 .getSystemService(TextClassificationManager.class); 49 mTcm.setTextClassifier(null); 50 mClassifier = mTcm.getTextClassifier(); 51 } 52 53 @Test 54 public void testSmartSelection() { 55 if (isTextClassifierDisabled()) return; 56 57 String text = "Contact me at droid (at) android.com"; 58 String selected = "droid"; 59 String suggested = "droid (at) android.com"; 60 int startIndex = text.indexOf(selected); 61 int endIndex = startIndex + selected.length(); 62 int smartStartIndex = text.indexOf(suggested); 63 int smartEndIndex = smartStartIndex + suggested.length(); 64 65 assertThat(mClassifier.suggestSelection(text, startIndex, endIndex, LOCALES), 66 isTextSelection(smartStartIndex, smartEndIndex, TextClassifier.TYPE_EMAIL)); 67 } 68 69 @Test 70 public void testSmartSelection_nullLocaleList() { 71 if (isTextClassifierDisabled()) return; 72 73 String text = "Contact me at droid (at) android.com"; 74 String selected = "droid"; 75 String suggested = "droid (at) android.com"; 76 int startIndex = text.indexOf(selected); 77 int endIndex = startIndex + selected.length(); 78 int smartStartIndex = text.indexOf(suggested); 79 int smartEndIndex = smartStartIndex + suggested.length(); 80 LocaleList nullLocales = null; 81 82 assertThat(mClassifier.suggestSelection(text, startIndex, endIndex, nullLocales), 83 isTextSelection(smartStartIndex, smartEndIndex, TextClassifier.TYPE_EMAIL)); 84 } 85 86 @Test 87 public void testSmartSelection_url() { 88 if (isTextClassifierDisabled()) return; 89 90 String text = "Visit http://www.android.com for more information"; 91 String selected = "http"; 92 String suggested = "http://www.android.com"; 93 int startIndex = text.indexOf(selected); 94 int endIndex = startIndex + selected.length(); 95 int smartStartIndex = text.indexOf(suggested); 96 int smartEndIndex = smartStartIndex + suggested.length(); 97 98 assertThat(mClassifier.suggestSelection(text, startIndex, endIndex, LOCALES), 99 isTextSelection(smartStartIndex, smartEndIndex, TextClassifier.TYPE_URL)); 100 } 101 102 @Test 103 public void testSmartSelection_withEmoji() { 104 if (isTextClassifierDisabled()) return; 105 106 String text = "\uD83D\uDE02 Hello."; 107 String selected = "Hello"; 108 int startIndex = text.indexOf(selected); 109 int endIndex = startIndex + selected.length(); 110 111 assertThat(mClassifier.suggestSelection(text, startIndex, endIndex, LOCALES), 112 isTextSelection(startIndex, endIndex, NO_TYPE)); 113 } 114 115 @Test 116 public void testClassifyText() { 117 if (isTextClassifierDisabled()) return; 118 119 String text = "Contact me at droid (at) android.com"; 120 String classifiedText = "droid (at) android.com"; 121 int startIndex = text.indexOf(classifiedText); 122 int endIndex = startIndex + classifiedText.length(); 123 assertThat(mClassifier.classifyText(text, startIndex, endIndex, LOCALES), 124 isTextClassification( 125 classifiedText, 126 TextClassifier.TYPE_EMAIL, 127 "mailto:" + classifiedText)); 128 } 129 130 @Test 131 public void testTextClassifyText_url() { 132 if (isTextClassifierDisabled()) return; 133 134 String text = "Visit www.android.com for more information"; 135 String classifiedText = "www.android.com"; 136 int startIndex = text.indexOf(classifiedText); 137 int endIndex = startIndex + classifiedText.length(); 138 assertThat(mClassifier.classifyText(text, startIndex, endIndex, LOCALES), 139 isTextClassification( 140 classifiedText, 141 TextClassifier.TYPE_URL, 142 "http://" + classifiedText)); 143 } 144 145 @Test 146 public void testTextClassifyText_url_inCaps() { 147 if (isTextClassifierDisabled()) return; 148 149 String text = "Visit HTTP://ANDROID.COM for more information"; 150 String classifiedText = "HTTP://ANDROID.COM"; 151 int startIndex = text.indexOf(classifiedText); 152 int endIndex = startIndex + classifiedText.length(); 153 assertThat(mClassifier.classifyText(text, startIndex, endIndex, LOCALES), 154 isTextClassification( 155 classifiedText, 156 TextClassifier.TYPE_URL, 157 "http://ANDROID.COM")); 158 } 159 160 @Test 161 public void testTextClassifyText_nullLocaleList() { 162 if (isTextClassifierDisabled()) return; 163 164 String text = "Contact me at droid (at) android.com"; 165 String classifiedText = "droid (at) android.com"; 166 int startIndex = text.indexOf(classifiedText); 167 int endIndex = startIndex + classifiedText.length(); 168 LocaleList nullLocales = null; 169 assertThat(mClassifier.classifyText(text, startIndex, endIndex, nullLocales), 170 isTextClassification( 171 classifiedText, 172 TextClassifier.TYPE_EMAIL, 173 "mailto:" + classifiedText)); 174 } 175 176 @Test 177 public void testSetTextClassifier() { 178 TextClassifier classifier = mock(TextClassifier.class); 179 mTcm.setTextClassifier(classifier); 180 assertEquals(classifier, mTcm.getTextClassifier()); 181 } 182 183 private boolean isTextClassifierDisabled() { 184 return mClassifier == TextClassifier.NO_OP; 185 } 186 187 private static Matcher<TextSelection> isTextSelection( 188 final int startIndex, final int endIndex, final String type) { 189 return new BaseMatcher<TextSelection>() { 190 @Override 191 public boolean matches(Object o) { 192 if (o instanceof TextSelection) { 193 TextSelection selection = (TextSelection) o; 194 return startIndex == selection.getSelectionStartIndex() 195 && endIndex == selection.getSelectionEndIndex() 196 && typeMatches(selection, type); 197 } 198 return false; 199 } 200 201 private boolean typeMatches(TextSelection selection, String type) { 202 return type == null 203 || (selection.getEntityCount() > 0 204 && type.trim().equalsIgnoreCase(selection.getEntity(0))); 205 } 206 207 @Override 208 public void describeTo(Description description) { 209 description.appendValue( 210 String.format("%d, %d, %s", startIndex, endIndex, type)); 211 } 212 }; 213 } 214 215 private static Matcher<TextClassification> isTextClassification( 216 final String text, final String type, final String intentUri) { 217 return new BaseMatcher<TextClassification>() { 218 @Override 219 public boolean matches(Object o) { 220 if (o instanceof TextClassification) { 221 TextClassification result = (TextClassification) o; 222 final boolean typeRequirementSatisfied; 223 String scheme; 224 switch (type) { 225 case TextClassifier.TYPE_EMAIL: 226 scheme = result.getIntent().getData().getScheme(); 227 typeRequirementSatisfied = "mailto".equals(scheme); 228 break; 229 case TextClassifier.TYPE_URL: 230 scheme = result.getIntent().getData().getScheme(); 231 typeRequirementSatisfied = "http".equals(scheme) 232 || "https".equals(scheme); 233 break; 234 default: 235 typeRequirementSatisfied = true; 236 } 237 238 return typeRequirementSatisfied 239 && text.equals(result.getText()) 240 && result.getEntityCount() > 0 241 && type.equals(result.getEntity(0)) 242 && intentUri.equals(result.getIntent().getDataString()); 243 // TODO: Include other properties. 244 } 245 return false; 246 } 247 248 @Override 249 public void describeTo(Description description) { 250 description.appendText("text=").appendValue(text) 251 .appendText(", type=").appendValue(type) 252 .appendText(", intent.data=").appendValue(intentUri); 253 } 254 }; 255 } 256 } 257