1 /* 2 * Copyright (C) 2015 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.textservice; 18 19 import android.os.Parcel; 20 import android.test.InstrumentationTestCase; 21 import android.test.suitebuilder.annotation.SmallTest; 22 23 import java.util.Arrays; 24 import java.util.Locale; 25 26 import static android.test.MoreAsserts.assertNotEqual; 27 28 /** 29 * TODO: Most of part can be, and probably should be, moved to CTS. 30 */ 31 public class SpellCheckerSubtypeTest extends InstrumentationTestCase { 32 private static final int SUBTYPE_SUBTYPE_ID_NONE = 0; 33 private static final String SUBTYPE_SUBTYPE_LOCALE_STRING_NONE = ""; 34 private static final String SUBTYPE_SUBTYPE_LANGUAGE_TAG_NONE = ""; 35 36 private static final String SUBTYPE_SUBTYPE_LOCALE_STRING_A = "en_GB"; 37 private static final String SUBTYPE_SUBTYPE_LANGUAGE_TAG_A = "en-GB"; 38 private static final int SUBTYPE_NAME_RES_ID_A = 0x12345; 39 private static final String SUBTYPE_EXTRA_VALUE_A = "Key1=Value1,Key2=Value2"; 40 private static final int SUBTYPE_SUBTYPE_ID_A = 42; 41 private static final String SUBTYPE_SUBTYPE_LOCALE_STRING_B = "en_IN"; 42 private static final String SUBTYPE_SUBTYPE_LANGUAGE_TAG_B = "en-IN"; 43 private static final int SUBTYPE_NAME_RES_ID_B = 0x54321; 44 private static final String SUBTYPE_EXTRA_VALUE_B = "Key3=Value3,Key4=Value4"; 45 private static final int SUBTYPE_SUBTYPE_ID_B = -42; 46 47 private static int defaultHashCodeAlgorithm(String locale, String extraValue) { 48 return Arrays.hashCode(new Object[] {locale, extraValue}); 49 } 50 51 private static final SpellCheckerSubtype cloneViaParcel(final SpellCheckerSubtype original) { 52 Parcel parcel = null; 53 try { 54 parcel = Parcel.obtain(); 55 original.writeToParcel(parcel, 0); 56 parcel.setDataPosition(0); 57 return SpellCheckerSubtype.CREATOR.createFromParcel(parcel); 58 } finally { 59 if (parcel != null) { 60 parcel.recycle(); 61 } 62 } 63 } 64 65 @SmallTest 66 public void testSubtypeWithNoSubtypeId() throws Exception { 67 final SpellCheckerSubtype subtype = new SpellCheckerSubtype(SUBTYPE_NAME_RES_ID_A, 68 SUBTYPE_SUBTYPE_LOCALE_STRING_A, SUBTYPE_SUBTYPE_LANGUAGE_TAG_A, 69 SUBTYPE_EXTRA_VALUE_A, SUBTYPE_SUBTYPE_ID_NONE); 70 assertEquals(SUBTYPE_NAME_RES_ID_A, subtype.getNameResId()); 71 assertEquals(SUBTYPE_SUBTYPE_LOCALE_STRING_A, subtype.getLocale()); 72 assertEquals(SUBTYPE_SUBTYPE_LANGUAGE_TAG_A, subtype.getLanguageTag()); 73 assertEquals("Value1", subtype.getExtraValueOf("Key1")); 74 assertEquals("Value2", subtype.getExtraValueOf("Key2")); 75 // Historically we have used SpellCheckerSubtype#hashCode() to track which subtype is 76 // enabled, and it is supposed to be stored in SecureSettings. Therefore we have to 77 // keep using the same algorithm for compatibility reasons. 78 assertEquals( 79 defaultHashCodeAlgorithm(SUBTYPE_SUBTYPE_LOCALE_STRING_A, SUBTYPE_EXTRA_VALUE_A), 80 subtype.hashCode()); 81 82 final SpellCheckerSubtype clonedSubtype = cloneViaParcel(subtype); 83 assertEquals(SUBTYPE_NAME_RES_ID_A, clonedSubtype.getNameResId()); 84 assertEquals(SUBTYPE_SUBTYPE_LOCALE_STRING_A, clonedSubtype.getLocale()); 85 assertEquals(SUBTYPE_SUBTYPE_LANGUAGE_TAG_A, clonedSubtype.getLanguageTag()); 86 assertEquals("Value1", clonedSubtype.getExtraValueOf("Key1")); 87 assertEquals("Value2", clonedSubtype.getExtraValueOf("Key2")); 88 assertEquals( 89 defaultHashCodeAlgorithm(SUBTYPE_SUBTYPE_LOCALE_STRING_A, SUBTYPE_EXTRA_VALUE_A), 90 clonedSubtype.hashCode()); 91 } 92 93 public void testSubtypeWithSubtypeId() throws Exception { 94 final SpellCheckerSubtype subtype = new SpellCheckerSubtype(SUBTYPE_NAME_RES_ID_A, 95 SUBTYPE_SUBTYPE_LOCALE_STRING_A, SUBTYPE_SUBTYPE_LANGUAGE_TAG_A, 96 SUBTYPE_EXTRA_VALUE_A, SUBTYPE_SUBTYPE_ID_A); 97 98 assertEquals(SUBTYPE_NAME_RES_ID_A, subtype.getNameResId()); 99 assertEquals(SUBTYPE_SUBTYPE_LOCALE_STRING_A, subtype.getLocale()); 100 assertEquals(SUBTYPE_SUBTYPE_LANGUAGE_TAG_A, subtype.getLanguageTag()); 101 assertEquals("Value1", subtype.getExtraValueOf("Key1")); 102 assertEquals("Value2", subtype.getExtraValueOf("Key2")); 103 // Similar to "SubtypeId" in InputMethodSubtype, "SubtypeId" in SpellCheckerSubtype enables 104 // developers to specify a stable and consistent ID for each subtype. 105 assertEquals(SUBTYPE_SUBTYPE_ID_A, subtype.hashCode()); 106 107 final SpellCheckerSubtype clonedSubtype = cloneViaParcel(subtype); 108 assertEquals(SUBTYPE_NAME_RES_ID_A, clonedSubtype.getNameResId()); 109 assertEquals(SUBTYPE_SUBTYPE_LOCALE_STRING_A, clonedSubtype.getLocale()); 110 assertEquals(SUBTYPE_SUBTYPE_LANGUAGE_TAG_A, clonedSubtype.getLanguageTag()); 111 assertEquals("Value1", clonedSubtype.getExtraValueOf("Key1")); 112 assertEquals("Value2", clonedSubtype.getExtraValueOf("Key2")); 113 assertEquals(SUBTYPE_SUBTYPE_ID_A, clonedSubtype.hashCode()); 114 } 115 116 @SmallTest 117 public void testGetLocaleObject() throws Exception { 118 assertEquals(new Locale("en", "GB"), 119 new SpellCheckerSubtype(SUBTYPE_NAME_RES_ID_A, "en_GB", 120 SUBTYPE_SUBTYPE_LANGUAGE_TAG_NONE, SUBTYPE_EXTRA_VALUE_A, 121 SUBTYPE_SUBTYPE_ID_NONE).getLocaleObject()); 122 assertEquals(new Locale("en", "GB"), 123 new SpellCheckerSubtype(SUBTYPE_NAME_RES_ID_A, SUBTYPE_SUBTYPE_LOCALE_STRING_NONE, 124 "en-GB", SUBTYPE_EXTRA_VALUE_A, 125 SUBTYPE_SUBTYPE_ID_NONE).getLocaleObject()); 126 127 // If neither locale string nor language tag is specified, 128 // {@link SpellCheckerSubtype#getLocaleObject} returns null. 129 assertNull( 130 new SpellCheckerSubtype(SUBTYPE_NAME_RES_ID_A, SUBTYPE_SUBTYPE_LOCALE_STRING_NONE, 131 SUBTYPE_SUBTYPE_LANGUAGE_TAG_NONE, SUBTYPE_EXTRA_VALUE_A, 132 SUBTYPE_SUBTYPE_ID_NONE).getLocaleObject()); 133 134 // If both locale string and language tag are specified, 135 // {@link SpellCheckerSubtype#getLocaleObject} uses language tag. 136 assertEquals(new Locale("en", "GB"), 137 new SpellCheckerSubtype(SUBTYPE_NAME_RES_ID_A, "en_US", "en-GB", 138 SUBTYPE_EXTRA_VALUE_A, SUBTYPE_SUBTYPE_ID_NONE).getLocaleObject()); 139 140 // Make sure that "tl_PH" is rewritten to "fil_PH" for spell checkers that need to support 141 // Android KitKat and prior, which do not support 3-letter language codes. 142 assertEquals(new Locale("fil", "PH"), 143 new SpellCheckerSubtype(SUBTYPE_NAME_RES_ID_A, "tl_PH", 144 SUBTYPE_SUBTYPE_LANGUAGE_TAG_NONE, SUBTYPE_EXTRA_VALUE_A, 145 SUBTYPE_SUBTYPE_ID_NONE).getLocaleObject()); 146 147 // "languageTag" attribute is available in Android N and later, where 3-letter country codes 148 // are guaranteed to be available. It's developers' responsibility for specifying a valid 149 // country subtags here and we do not rewrite "tl" to "fil" for simplicity. 150 assertEquals("tl", 151 new SpellCheckerSubtype(SUBTYPE_NAME_RES_ID_A, SUBTYPE_SUBTYPE_LOCALE_STRING_NONE, 152 "tl-PH", SUBTYPE_EXTRA_VALUE_A, SUBTYPE_SUBTYPE_ID_NONE) 153 .getLocaleObject().getLanguage()); 154 } 155 156 @SmallTest 157 public void testEquality() throws Exception { 158 assertEquals( 159 new SpellCheckerSubtype(SUBTYPE_NAME_RES_ID_A, SUBTYPE_SUBTYPE_LOCALE_STRING_A, 160 SUBTYPE_EXTRA_VALUE_A), 161 new SpellCheckerSubtype(SUBTYPE_NAME_RES_ID_A, SUBTYPE_SUBTYPE_LOCALE_STRING_A, 162 SUBTYPE_EXTRA_VALUE_A)); 163 assertNotEqual( 164 new SpellCheckerSubtype(SUBTYPE_NAME_RES_ID_A, SUBTYPE_SUBTYPE_LOCALE_STRING_A, 165 SUBTYPE_EXTRA_VALUE_A), 166 new SpellCheckerSubtype(SUBTYPE_NAME_RES_ID_B, SUBTYPE_SUBTYPE_LOCALE_STRING_B, 167 SUBTYPE_EXTRA_VALUE_A)); 168 assertNotEqual( 169 new SpellCheckerSubtype(SUBTYPE_NAME_RES_ID_A, SUBTYPE_SUBTYPE_LOCALE_STRING_A, 170 SUBTYPE_EXTRA_VALUE_A), 171 new SpellCheckerSubtype(SUBTYPE_NAME_RES_ID_A, SUBTYPE_SUBTYPE_LOCALE_STRING_B, 172 SUBTYPE_EXTRA_VALUE_A)); 173 assertNotEqual( 174 new SpellCheckerSubtype(SUBTYPE_NAME_RES_ID_A, SUBTYPE_SUBTYPE_LOCALE_STRING_A, 175 SUBTYPE_EXTRA_VALUE_A), 176 new SpellCheckerSubtype(SUBTYPE_NAME_RES_ID_A, SUBTYPE_SUBTYPE_LOCALE_STRING_A, 177 SUBTYPE_EXTRA_VALUE_B)); 178 179 // If subtype ID is 0 (== SUBTYPE_SUBTYPE_ID_NONE), we keep the same behavior. 180 assertEquals( 181 new SpellCheckerSubtype(SUBTYPE_NAME_RES_ID_A, SUBTYPE_SUBTYPE_LOCALE_STRING_A, 182 SUBTYPE_SUBTYPE_LANGUAGE_TAG_A, SUBTYPE_EXTRA_VALUE_A, 183 SUBTYPE_SUBTYPE_ID_NONE), 184 new SpellCheckerSubtype(SUBTYPE_NAME_RES_ID_A, SUBTYPE_SUBTYPE_LOCALE_STRING_A, 185 SUBTYPE_SUBTYPE_LANGUAGE_TAG_A, SUBTYPE_EXTRA_VALUE_A, 186 SUBTYPE_SUBTYPE_ID_NONE)); 187 assertNotEqual( 188 new SpellCheckerSubtype(SUBTYPE_NAME_RES_ID_A, SUBTYPE_SUBTYPE_LOCALE_STRING_A, 189 SUBTYPE_SUBTYPE_LANGUAGE_TAG_A, SUBTYPE_EXTRA_VALUE_A, 190 SUBTYPE_SUBTYPE_ID_NONE), 191 new SpellCheckerSubtype(SUBTYPE_NAME_RES_ID_B, SUBTYPE_SUBTYPE_LOCALE_STRING_B, 192 SUBTYPE_SUBTYPE_LANGUAGE_TAG_A, SUBTYPE_EXTRA_VALUE_A, 193 SUBTYPE_SUBTYPE_ID_NONE)); 194 assertNotEqual( 195 new SpellCheckerSubtype(SUBTYPE_NAME_RES_ID_A, SUBTYPE_SUBTYPE_LOCALE_STRING_A, 196 SUBTYPE_SUBTYPE_LANGUAGE_TAG_A, SUBTYPE_EXTRA_VALUE_A, 197 SUBTYPE_SUBTYPE_ID_NONE), 198 new SpellCheckerSubtype(SUBTYPE_NAME_RES_ID_A, SUBTYPE_SUBTYPE_LOCALE_STRING_B, 199 SUBTYPE_SUBTYPE_LANGUAGE_TAG_A, SUBTYPE_EXTRA_VALUE_A, 200 SUBTYPE_SUBTYPE_ID_NONE)); 201 assertNotEqual( 202 new SpellCheckerSubtype(SUBTYPE_NAME_RES_ID_A, SUBTYPE_SUBTYPE_LOCALE_STRING_A, 203 SUBTYPE_SUBTYPE_LANGUAGE_TAG_A, SUBTYPE_EXTRA_VALUE_A, 204 SUBTYPE_SUBTYPE_ID_NONE), 205 new SpellCheckerSubtype(SUBTYPE_NAME_RES_ID_A, SUBTYPE_SUBTYPE_LOCALE_STRING_A, 206 SUBTYPE_SUBTYPE_LANGUAGE_TAG_B, SUBTYPE_EXTRA_VALUE_A, 207 SUBTYPE_SUBTYPE_ID_NONE)); 208 assertNotEqual( 209 new SpellCheckerSubtype(SUBTYPE_NAME_RES_ID_A, SUBTYPE_SUBTYPE_LOCALE_STRING_A, 210 SUBTYPE_SUBTYPE_LANGUAGE_TAG_A, SUBTYPE_EXTRA_VALUE_A, 211 SUBTYPE_SUBTYPE_ID_NONE), 212 new SpellCheckerSubtype(SUBTYPE_NAME_RES_ID_A, SUBTYPE_SUBTYPE_LOCALE_STRING_A, 213 SUBTYPE_SUBTYPE_LANGUAGE_TAG_A, SUBTYPE_EXTRA_VALUE_B, 214 SUBTYPE_SUBTYPE_ID_NONE)); 215 216 // If subtype ID is not 0, we test the equality based only on the subtype ID. 217 assertEquals( 218 new SpellCheckerSubtype(SUBTYPE_NAME_RES_ID_A, SUBTYPE_SUBTYPE_LOCALE_STRING_A, 219 SUBTYPE_SUBTYPE_LANGUAGE_TAG_A, SUBTYPE_EXTRA_VALUE_A, 220 SUBTYPE_SUBTYPE_ID_A), 221 new SpellCheckerSubtype(SUBTYPE_NAME_RES_ID_A, SUBTYPE_SUBTYPE_LOCALE_STRING_A, 222 SUBTYPE_SUBTYPE_LANGUAGE_TAG_A, SUBTYPE_EXTRA_VALUE_A, 223 SUBTYPE_SUBTYPE_ID_A)); 224 assertEquals( 225 new SpellCheckerSubtype(SUBTYPE_NAME_RES_ID_A, SUBTYPE_SUBTYPE_LOCALE_STRING_A, 226 SUBTYPE_SUBTYPE_LANGUAGE_TAG_A, SUBTYPE_EXTRA_VALUE_A, 227 SUBTYPE_SUBTYPE_ID_A), 228 new SpellCheckerSubtype(SUBTYPE_NAME_RES_ID_B, SUBTYPE_SUBTYPE_LOCALE_STRING_B, 229 SUBTYPE_SUBTYPE_LANGUAGE_TAG_A, SUBTYPE_EXTRA_VALUE_A, 230 SUBTYPE_SUBTYPE_ID_A)); 231 assertEquals( 232 new SpellCheckerSubtype(SUBTYPE_NAME_RES_ID_A, SUBTYPE_SUBTYPE_LOCALE_STRING_A, 233 SUBTYPE_SUBTYPE_LANGUAGE_TAG_A, SUBTYPE_EXTRA_VALUE_A, 234 SUBTYPE_SUBTYPE_ID_A), 235 new SpellCheckerSubtype(SUBTYPE_NAME_RES_ID_A, SUBTYPE_SUBTYPE_LOCALE_STRING_B, 236 SUBTYPE_SUBTYPE_LANGUAGE_TAG_A, SUBTYPE_EXTRA_VALUE_A, 237 SUBTYPE_SUBTYPE_ID_A)); 238 assertEquals( 239 new SpellCheckerSubtype(SUBTYPE_NAME_RES_ID_A, SUBTYPE_SUBTYPE_LOCALE_STRING_A, 240 SUBTYPE_SUBTYPE_LANGUAGE_TAG_A, SUBTYPE_EXTRA_VALUE_A, 241 SUBTYPE_SUBTYPE_ID_A), 242 new SpellCheckerSubtype(SUBTYPE_NAME_RES_ID_A, SUBTYPE_SUBTYPE_LOCALE_STRING_A, 243 SUBTYPE_SUBTYPE_LANGUAGE_TAG_B, SUBTYPE_EXTRA_VALUE_A, 244 SUBTYPE_SUBTYPE_ID_A)); 245 assertEquals( 246 new SpellCheckerSubtype(SUBTYPE_NAME_RES_ID_A, SUBTYPE_SUBTYPE_LOCALE_STRING_A, 247 SUBTYPE_SUBTYPE_LANGUAGE_TAG_A, SUBTYPE_EXTRA_VALUE_A, 248 SUBTYPE_SUBTYPE_ID_A), 249 new SpellCheckerSubtype(SUBTYPE_NAME_RES_ID_A, SUBTYPE_SUBTYPE_LOCALE_STRING_A, 250 SUBTYPE_SUBTYPE_LANGUAGE_TAG_A, SUBTYPE_EXTRA_VALUE_B, 251 SUBTYPE_SUBTYPE_ID_A)); 252 assertNotEqual( 253 new SpellCheckerSubtype(SUBTYPE_NAME_RES_ID_A, SUBTYPE_SUBTYPE_LOCALE_STRING_A, 254 SUBTYPE_SUBTYPE_LANGUAGE_TAG_A, SUBTYPE_EXTRA_VALUE_A, 255 SUBTYPE_SUBTYPE_ID_A), 256 new SpellCheckerSubtype(SUBTYPE_NAME_RES_ID_A, SUBTYPE_SUBTYPE_LOCALE_STRING_A, 257 SUBTYPE_SUBTYPE_LANGUAGE_TAG_A, SUBTYPE_EXTRA_VALUE_A, 258 SUBTYPE_SUBTYPE_ID_B)); 259 } 260 261 } 262