1 /* 2 * Copyright (C) 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 */ 16 17 package com.android.textclassifier; 18 19 import static com.google.common.truth.Truth.assertThat; 20 21 import android.app.UiAutomation; 22 import android.content.pm.PackageManager; 23 import android.content.pm.PackageManager.NameNotFoundException; 24 import android.icu.util.ULocale; 25 import android.provider.DeviceConfig; 26 import android.view.textclassifier.ConversationAction; 27 import android.view.textclassifier.ConversationActions; 28 import android.view.textclassifier.TextClassification; 29 import android.view.textclassifier.TextClassificationManager; 30 import android.view.textclassifier.TextClassifier; 31 import android.view.textclassifier.TextLanguage; 32 import android.view.textclassifier.TextLinks; 33 import android.view.textclassifier.TextLinks.TextLink; 34 import android.view.textclassifier.TextSelection; 35 import androidx.test.core.app.ApplicationProvider; 36 import androidx.test.ext.junit.runners.AndroidJUnit4; 37 import androidx.test.filters.SmallTest; 38 import androidx.test.platform.app.InstrumentationRegistry; 39 import com.google.common.collect.ImmutableList; 40 import java.util.ArrayList; 41 import java.util.List; 42 import org.junit.Before; 43 import org.junit.Rule; 44 import org.junit.Test; 45 import org.junit.rules.ExternalResource; 46 import org.junit.runner.RunWith; 47 48 /** 49 * End-to-end tests for the {@link TextClassifier} APIs. Unlike {@link TextClassifierImplTest}. 50 * 51 * <p>Unlike {@link TextClassifierImplTest}, we are trying to run the tests in a environment that is 52 * closer to the production environment. For example, we are not injecting the model files. 53 */ 54 @SmallTest 55 @RunWith(AndroidJUnit4.class) 56 public class TextClassifierApiTest { 57 58 private TextClassifier textClassifier; 59 60 @Rule 61 public final ExtServicesTextClassifierRule extServicesTextClassifierRule = 62 new ExtServicesTextClassifierRule(); 63 64 @Before setup()65 public void setup() { 66 textClassifier = extServicesTextClassifierRule.getTextClassifier(); 67 } 68 69 @Test suggestSelection()70 public void suggestSelection() { 71 String text = "Visit http://www.android.com for more information"; 72 String selected = "http"; 73 String suggested = "http://www.android.com"; 74 int startIndex = text.indexOf(selected); 75 int endIndex = startIndex + selected.length(); 76 int smartStartIndex = text.indexOf(suggested); 77 int smartEndIndex = smartStartIndex + suggested.length(); 78 79 TextSelection.Request request = 80 new TextSelection.Request.Builder(text, startIndex, endIndex).build(); 81 82 TextSelection selection = textClassifier.suggestSelection(request); 83 assertThat(selection.getEntityCount()).isGreaterThan(0); 84 assertThat(selection.getEntity(0)).isEqualTo(TextClassifier.TYPE_URL); 85 assertThat(selection.getSelectionStartIndex()).isEqualTo(smartStartIndex); 86 assertThat(selection.getSelectionEndIndex()).isEqualTo(smartEndIndex); 87 } 88 89 @Test classifyText()90 public void classifyText() { 91 String text = "Contact me at droid@android.com"; 92 String classifiedText = "droid@android.com"; 93 int startIndex = text.indexOf(classifiedText); 94 int endIndex = startIndex + classifiedText.length(); 95 TextClassification.Request request = 96 new TextClassification.Request.Builder(text, startIndex, endIndex).build(); 97 98 TextClassification classification = textClassifier.classifyText(request); 99 assertThat(classification.getEntityCount()).isGreaterThan(0); 100 assertThat(classification.getEntity(0)).isEqualTo(TextClassifier.TYPE_EMAIL); 101 assertThat(classification.getText()).isEqualTo(classifiedText); 102 assertThat(classification.getActions()).isNotEmpty(); 103 } 104 105 @Test generateLinks()106 public void generateLinks() { 107 String text = "Check this out, http://www.android.com"; 108 109 TextLinks.Request request = new TextLinks.Request.Builder(text).build(); 110 111 TextLinks textLinks = textClassifier.generateLinks(request); 112 113 List<TextLink> links = new ArrayList<>(textLinks.getLinks()); 114 assertThat(textLinks.getText().toString()).isEqualTo(text); 115 assertThat(links).hasSize(1); 116 assertThat(links.get(0).getEntityCount()).isGreaterThan(0); 117 assertThat(links.get(0).getEntity(0)).isEqualTo(TextClassifier.TYPE_URL); 118 assertThat(links.get(0).getConfidenceScore(TextClassifier.TYPE_URL)).isGreaterThan(0f); 119 } 120 121 @Test detectedLanguage()122 public void detectedLanguage() { 123 String text = "朝、ピカチュウ"; 124 TextLanguage.Request request = new TextLanguage.Request.Builder(text).build(); 125 126 TextLanguage textLanguage = textClassifier.detectLanguage(request); 127 128 assertThat(textLanguage.getLocaleHypothesisCount()).isGreaterThan(0); 129 assertThat(textLanguage.getLocale(0).getLanguage()).isEqualTo("ja"); 130 assertThat(textLanguage.getConfidenceScore(ULocale.JAPANESE)).isGreaterThan(0f); 131 } 132 133 @Test suggestConversationActions()134 public void suggestConversationActions() { 135 ConversationActions.Message message = 136 new ConversationActions.Message.Builder(ConversationActions.Message.PERSON_USER_OTHERS) 137 .setText("Check this out: https://www.android.com") 138 .build(); 139 ConversationActions.Request request = 140 new ConversationActions.Request.Builder(ImmutableList.of(message)).build(); 141 142 ConversationActions conversationActions = textClassifier.suggestConversationActions(request); 143 144 assertThat(conversationActions.getConversationActions()).hasSize(1); 145 ConversationAction conversationAction = conversationActions.getConversationActions().get(0); 146 assertThat(conversationAction.getType()).isEqualTo(ConversationAction.TYPE_OPEN_URL); 147 assertThat(conversationAction.getAction()).isNotNull(); 148 } 149 150 /** A rule that manages a text classifier that is backed by the ExtServices. */ 151 private static class ExtServicesTextClassifierRule extends ExternalResource { 152 private static final String CONFIG_TEXT_CLASSIFIER_SERVICE_PACKAGE_OVERRIDE = 153 "textclassifier_service_package_override"; 154 private static final String PKG_NAME_GOOGLE_EXTSERVICES = "com.google.android.ext.services"; 155 private static final String PKG_NAME_AOSP_EXTSERVICES = "android.ext.services"; 156 157 private String textClassifierServiceOverrideFlagOldValue; 158 159 @Override before()160 protected void before() { 161 UiAutomation uiAutomation = InstrumentationRegistry.getInstrumentation().getUiAutomation(); 162 try { 163 uiAutomation.adoptShellPermissionIdentity(); 164 textClassifierServiceOverrideFlagOldValue = 165 DeviceConfig.getString( 166 DeviceConfig.NAMESPACE_TEXTCLASSIFIER, 167 CONFIG_TEXT_CLASSIFIER_SERVICE_PACKAGE_OVERRIDE, 168 null); 169 DeviceConfig.setProperty( 170 DeviceConfig.NAMESPACE_TEXTCLASSIFIER, 171 CONFIG_TEXT_CLASSIFIER_SERVICE_PACKAGE_OVERRIDE, 172 getExtServicesPackageName(), 173 /* makeDefault= */ false); 174 } finally { 175 uiAutomation.dropShellPermissionIdentity(); 176 } 177 } 178 179 @Override after()180 protected void after() { 181 UiAutomation uiAutomation = InstrumentationRegistry.getInstrumentation().getUiAutomation(); 182 try { 183 uiAutomation.adoptShellPermissionIdentity(); 184 DeviceConfig.setProperty( 185 DeviceConfig.NAMESPACE_TEXTCLASSIFIER, 186 CONFIG_TEXT_CLASSIFIER_SERVICE_PACKAGE_OVERRIDE, 187 textClassifierServiceOverrideFlagOldValue, 188 /* makeDefault= */ false); 189 } finally { 190 uiAutomation.dropShellPermissionIdentity(); 191 } 192 } 193 getExtServicesPackageName()194 private static String getExtServicesPackageName() { 195 PackageManager packageManager = 196 ApplicationProvider.getApplicationContext().getPackageManager(); 197 try { 198 packageManager.getApplicationInfo(PKG_NAME_GOOGLE_EXTSERVICES, /* flags= */ 0); 199 return PKG_NAME_GOOGLE_EXTSERVICES; 200 } catch (NameNotFoundException e) { 201 return PKG_NAME_AOSP_EXTSERVICES; 202 } 203 } 204 getTextClassifier()205 public TextClassifier getTextClassifier() { 206 TextClassificationManager textClassificationManager = 207 ApplicationProvider.getApplicationContext() 208 .getSystemService(TextClassificationManager.class); 209 return textClassificationManager.getTextClassifier(); 210 } 211 } 212 } 213