1 /* 2 * Copyright (C) 2014 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.inputmethod.keyboard.internal; 18 19 import static com.android.inputmethod.keyboard.internal.KeyboardCodesSet.PREFIX_CODE; 20 import static com.android.inputmethod.keyboard.internal.KeyboardIconsSet.ICON_UNDEFINED; 21 import static com.android.inputmethod.keyboard.internal.KeyboardIconsSet.PREFIX_ICON; 22 import static com.android.inputmethod.latin.common.Constants.CODE_OUTPUT_TEXT; 23 import static com.android.inputmethod.latin.common.Constants.CODE_UNSPECIFIED; 24 25 import android.test.AndroidTestCase; 26 27 import java.util.Locale; 28 29 abstract class KeySpecParserTestsBase extends AndroidTestCase { 30 private final static Locale TEST_LOCALE = Locale.ENGLISH; 31 protected final KeyboardTextsSet mTextsSet = new KeyboardTextsSet(); 32 33 private static final String CODE_SETTINGS_NAME = "key_settings"; 34 private static final String CODE_SETTINGS = PREFIX_CODE + CODE_SETTINGS_NAME; 35 private static final String ICON_SETTINGS_NAME = "settings_key"; 36 private static final String ICON_SETTINGS = PREFIX_ICON + ICON_SETTINGS_NAME; 37 private static final String CODE_SETTINGS_UPPERCASE = CODE_SETTINGS.toUpperCase(Locale.ROOT); 38 private static final String ICON_SETTINGS_UPPERCASE = ICON_SETTINGS.toUpperCase(Locale.ROOT); 39 private static final String CODE_NON_EXISTING = PREFIX_CODE + "non_existing"; 40 private static final String ICON_NON_EXISTING = PREFIX_ICON + "non_existing"; 41 42 private int mCodeSettings; 43 private int mCodeActionNext; 44 private int mSettingsIconId; 45 46 @Override 47 protected void setUp() throws Exception { 48 super.setUp(); 49 50 mTextsSet.setLocale(TEST_LOCALE, getContext()); 51 mCodeSettings = KeyboardCodesSet.getCode(CODE_SETTINGS_NAME); 52 mCodeActionNext = KeyboardCodesSet.getCode("key_action_next"); 53 mSettingsIconId = KeyboardIconsSet.getIconId(ICON_SETTINGS_NAME); 54 } 55 56 abstract protected void assertParser(final String message, final String keySpec, 57 final String expectedLabel, final String expectedOutputText, final int expectedIcon, 58 final int expectedCode); 59 60 protected void assertParserError(final String message, final String keySpec, 61 final String expectedLabel, final String expectedOutputText, final int expectedIconId, 62 final int expectedCode) { 63 try { 64 assertParser(message, keySpec, expectedLabel, expectedOutputText, expectedIconId, 65 expectedCode); 66 fail(message); 67 } catch (Exception pcpe) { 68 // success. 69 } 70 } 71 72 // \U001d11e: MUSICAL SYMBOL G CLEF 73 private static final String SURROGATE_PAIR1 = "\ud834\udd1e"; 74 private static final int SURROGATE_CODE1 = SURROGATE_PAIR1.codePointAt(0); 75 // \U001d122: MUSICAL SYMBOL F CLEF 76 private static final String SURROGATE_PAIR2 = "\ud834\udd22"; 77 private static final int SURROGATE_CODE2 = SURROGATE_PAIR2.codePointAt(0); 78 // \U002f8a6: CJK COMPATIBILITY IDEOGRAPH-2F8A6; variant character of \u6148. 79 private static final String SURROGATE_PAIR3 = "\ud87e\udca6"; 80 private static final String SURROGATE_PAIRS4 = SURROGATE_PAIR1 + SURROGATE_PAIR2; 81 private static final String SURROGATE_PAIRS5 = SURROGATE_PAIRS4 + SURROGATE_PAIR3; 82 83 public void testSingleLetter() { 84 assertParser("Single letter", "a", 85 "a", null, ICON_UNDEFINED, 'a'); 86 assertParser("Single surrogate", SURROGATE_PAIR1, 87 SURROGATE_PAIR1, null, ICON_UNDEFINED, SURROGATE_CODE1); 88 assertParser("Sole vertical bar", "|", 89 "|", null, ICON_UNDEFINED, '|'); 90 assertParser("Single escaped vertical bar", "\\|", 91 "|", null, ICON_UNDEFINED, '|'); 92 assertParser("Single escaped escape", "\\\\", 93 "\\", null, ICON_UNDEFINED, '\\'); 94 assertParser("Single comma", ",", 95 ",", null, ICON_UNDEFINED, ','); 96 assertParser("Single escaped comma", "\\,", 97 ",", null, ICON_UNDEFINED, ','); 98 assertParser("Single escaped letter", "\\a", 99 "a", null, ICON_UNDEFINED, 'a'); 100 assertParser("Single escaped surrogate", "\\" + SURROGATE_PAIR2, 101 SURROGATE_PAIR2, null, ICON_UNDEFINED, SURROGATE_CODE2); 102 assertParser("Single bang", "!", 103 "!", null, ICON_UNDEFINED, '!'); 104 assertParser("Single escaped bang", "\\!", 105 "!", null, ICON_UNDEFINED, '!'); 106 assertParser("Single output text letter", "a|a", 107 "a", null, ICON_UNDEFINED, 'a'); 108 assertParser("Single surrogate pair outputText", "G Clef|" + SURROGATE_PAIR1, 109 "G Clef", null, ICON_UNDEFINED, SURROGATE_CODE1); 110 assertParser("Single letter with outputText", "a|abc", 111 "a", "abc", ICON_UNDEFINED, CODE_OUTPUT_TEXT); 112 assertParser("Single letter with surrogate outputText", "a|" + SURROGATE_PAIRS4, 113 "a", SURROGATE_PAIRS4, ICON_UNDEFINED, CODE_OUTPUT_TEXT); 114 assertParser("Single surrogate with outputText", SURROGATE_PAIR3 + "|abc", 115 SURROGATE_PAIR3, "abc", ICON_UNDEFINED, CODE_OUTPUT_TEXT); 116 assertParser("Single letter with escaped outputText", "a|a\\|c", 117 "a", "a|c", ICON_UNDEFINED, CODE_OUTPUT_TEXT); 118 assertParser("Single letter with escaped surrogate outputText", 119 "a|" + SURROGATE_PAIR1 + "\\|" + SURROGATE_PAIR2, 120 "a", SURROGATE_PAIR1 + "|" + SURROGATE_PAIR2, ICON_UNDEFINED, CODE_OUTPUT_TEXT); 121 assertParser("Single letter with comma outputText", "a|a,b", 122 "a", "a,b", ICON_UNDEFINED, CODE_OUTPUT_TEXT); 123 assertParser("Single letter with escaped comma outputText", "a|a\\,b", 124 "a", "a,b", ICON_UNDEFINED, CODE_OUTPUT_TEXT); 125 assertParser("Single letter with outputText starts with bang", "a|!bc", 126 "a", "!bc", ICON_UNDEFINED, CODE_OUTPUT_TEXT); 127 assertParser("Single letter with surrogate outputText starts with bang", 128 "a|!" + SURROGATE_PAIRS5, 129 "a", "!" + SURROGATE_PAIRS5, ICON_UNDEFINED, CODE_OUTPUT_TEXT); 130 assertParser("Single letter with outputText contains bang", "a|a!c", 131 "a", "a!c", ICON_UNDEFINED, CODE_OUTPUT_TEXT); 132 assertParser("Single letter with escaped bang outputText", "a|\\!bc", 133 "a", "!bc", ICON_UNDEFINED, CODE_OUTPUT_TEXT); 134 assertParser("Single escaped escape with single outputText", "\\\\|\\\\", 135 "\\", null, ICON_UNDEFINED, '\\'); 136 assertParser("Single escaped bar with single outputText", "\\||\\|", 137 "|", null, ICON_UNDEFINED, '|'); 138 assertParser("Single letter with code", "a|" + CODE_SETTINGS, 139 "a", null, ICON_UNDEFINED, mCodeSettings); 140 } 141 142 public void testLabel() { 143 assertParser("Simple label", "abc", 144 "abc", "abc", ICON_UNDEFINED, CODE_OUTPUT_TEXT); 145 assertParser("Simple surrogate label", SURROGATE_PAIRS4, 146 SURROGATE_PAIRS4, SURROGATE_PAIRS4, ICON_UNDEFINED, CODE_OUTPUT_TEXT); 147 assertParser("Label with escaped bar", "a\\|c", 148 "a|c", "a|c", ICON_UNDEFINED, CODE_OUTPUT_TEXT); 149 assertParser("Surrogate label with escaped bar", SURROGATE_PAIR1 + "\\|" + SURROGATE_PAIR2, 150 SURROGATE_PAIR1 + "|" + SURROGATE_PAIR2, SURROGATE_PAIR1 + "|" + SURROGATE_PAIR2, 151 ICON_UNDEFINED, CODE_OUTPUT_TEXT); 152 assertParser("Label with escaped escape", "a\\\\c", 153 "a\\c", "a\\c", ICON_UNDEFINED, CODE_OUTPUT_TEXT); 154 assertParser("Label with comma", "a,c", 155 "a,c", "a,c", ICON_UNDEFINED, CODE_OUTPUT_TEXT); 156 assertParser("Label with escaped comma", "a\\,c", 157 "a,c", "a,c", ICON_UNDEFINED, CODE_OUTPUT_TEXT); 158 assertParser("Label starts with bang", "!bc", 159 "!bc", "!bc", ICON_UNDEFINED, CODE_OUTPUT_TEXT); 160 assertParser("Surrogate label starts with bang", "!" + SURROGATE_PAIRS4, 161 "!" + SURROGATE_PAIRS4, "!" + SURROGATE_PAIRS4, ICON_UNDEFINED, CODE_OUTPUT_TEXT); 162 assertParser("Label contains bang", "a!c", 163 "a!c", "a!c", ICON_UNDEFINED, CODE_OUTPUT_TEXT); 164 assertParser("Label with escaped bang", "\\!bc", 165 "!bc", "!bc", ICON_UNDEFINED, CODE_OUTPUT_TEXT); 166 assertParser("Label with escaped letter", "\\abc", 167 "abc", "abc", ICON_UNDEFINED, CODE_OUTPUT_TEXT); 168 assertParser("Label with outputText", "abc|def", 169 "abc", "def", ICON_UNDEFINED, CODE_OUTPUT_TEXT); 170 assertParser("Label with comma and outputText", "a,c|def", 171 "a,c", "def", ICON_UNDEFINED, CODE_OUTPUT_TEXT); 172 assertParser("Escaped comma label with outputText", "a\\,c|def", 173 "a,c", "def", ICON_UNDEFINED, CODE_OUTPUT_TEXT); 174 assertParser("Escaped label with outputText", "a\\|c|def", 175 "a|c", "def", ICON_UNDEFINED, CODE_OUTPUT_TEXT); 176 assertParser("Label with escaped bar outputText", "abc|d\\|f", 177 "abc", "d|f", ICON_UNDEFINED, CODE_OUTPUT_TEXT); 178 assertParser("Escaped escape label with outputText", "a\\\\|def", 179 "a\\", "def", ICON_UNDEFINED, CODE_OUTPUT_TEXT); 180 assertParser("Label starts with bang and outputText", "!bc|def", 181 "!bc", "def", ICON_UNDEFINED, CODE_OUTPUT_TEXT); 182 assertParser("Label contains bang label and outputText", "a!c|def", 183 "a!c", "def", ICON_UNDEFINED, CODE_OUTPUT_TEXT); 184 assertParser("Escaped bang label with outputText", "\\!bc|def", 185 "!bc", "def", ICON_UNDEFINED, CODE_OUTPUT_TEXT); 186 assertParser("Label with comma outputText", "abc|a,b", 187 "abc", "a,b", ICON_UNDEFINED, CODE_OUTPUT_TEXT); 188 assertParser("Label with escaped comma outputText", "abc|a\\,b", 189 "abc", "a,b", ICON_UNDEFINED, CODE_OUTPUT_TEXT); 190 assertParser("Label with outputText starts with bang", "abc|!bc", 191 "abc", "!bc", ICON_UNDEFINED, CODE_OUTPUT_TEXT); 192 assertParser("Label with outputText contains bang", "abc|a!c", 193 "abc", "a!c", ICON_UNDEFINED, CODE_OUTPUT_TEXT); 194 assertParser("Label with escaped bang outputText", "abc|\\!bc", 195 "abc", "!bc", ICON_UNDEFINED, CODE_OUTPUT_TEXT); 196 assertParser("Label with escaped bar outputText", "abc|d\\|f", 197 "abc", "d|f", ICON_UNDEFINED, CODE_OUTPUT_TEXT); 198 assertParser("Escaped bar label with escaped bar outputText", "a\\|c|d\\|f", 199 "a|c", "d|f", ICON_UNDEFINED, CODE_OUTPUT_TEXT); 200 assertParser("Label with code", "abc|" + CODE_SETTINGS, 201 "abc", null, ICON_UNDEFINED, mCodeSettings); 202 assertParser("Escaped label with code", "a\\|c|" + CODE_SETTINGS, 203 "a|c", null, ICON_UNDEFINED, mCodeSettings); 204 } 205 206 public void testCodes() { 207 assertParser("Hexadecimal code", "a|0x1000", 208 "a", null, ICON_UNDEFINED, 0x1000); 209 assertParserError("Illegal hexadecimal code", "a|0x100X", 210 "a", null, ICON_UNDEFINED, CODE_UNSPECIFIED); 211 assertParser("Escaped hexadecimal code 1", "a|\\0x1000", 212 "a", "0x1000", ICON_UNDEFINED, CODE_OUTPUT_TEXT); 213 assertParser("Escaped hexadecimal code 2", "a|0\\x1000", 214 "a", "0x1000", ICON_UNDEFINED, CODE_OUTPUT_TEXT); 215 assertParser("Escaped hexadecimal code 2", "a|0\\x1000", 216 "a", "0x1000", ICON_UNDEFINED, CODE_OUTPUT_TEXT); 217 assertParserError("Illegally escaped hexadecimal code", "a|0x1\\000", 218 "a", null, ICON_UNDEFINED, CODE_UNSPECIFIED); 219 // This is a workaround to have a key that has a supplementary code point. We can't put a 220 // string in resource as a XML entity of a supplementary code point or a surrogate pair. 221 // TODO: Should pass this test. 222 // assertParser("Hexadecimal supplementary code", String.format("a|0x%06x", SURROGATE_CODE2), 223 // SURROGATE_PAIR2, null, ICON_UNDEFINED, SURROGATE_CODE2); 224 assertParser("Zero is treated as output text", "a|0", 225 "a", null, ICON_UNDEFINED, '0'); 226 assertParser("Digit is treated as output text", "a|3", 227 "a", null, ICON_UNDEFINED, '3'); 228 assertParser("Decimal number is treated as an output text", "a|2014", 229 "a", "2014", ICON_UNDEFINED, CODE_OUTPUT_TEXT); 230 } 231 232 public void testIcons() { 233 assertParser("Icon with single letter", ICON_SETTINGS + "|a", 234 null, null, mSettingsIconId, 'a'); 235 assertParser("Icon with outputText", ICON_SETTINGS + "|abc", 236 null, "abc", mSettingsIconId, CODE_OUTPUT_TEXT); 237 assertParser("Icon with outputText starts with bang", ICON_SETTINGS + "|!bc", 238 null, "!bc", mSettingsIconId, CODE_OUTPUT_TEXT); 239 assertParser("Icon with outputText contains bang", ICON_SETTINGS + "|a!c", 240 null, "a!c", mSettingsIconId, CODE_OUTPUT_TEXT); 241 assertParser("Icon with escaped bang outputText", ICON_SETTINGS + "|\\!bc", 242 null, "!bc", mSettingsIconId, CODE_OUTPUT_TEXT); 243 assertParser("Label starts with bang and code", "!bc|" + CODE_SETTINGS, 244 "!bc", null, ICON_UNDEFINED, mCodeSettings); 245 assertParser("Label contains bang and code", "a!c|" + CODE_SETTINGS, 246 "a!c", null, ICON_UNDEFINED, mCodeSettings); 247 assertParser("Escaped bang label with code", "\\!bc|" + CODE_SETTINGS, 248 "!bc", null, ICON_UNDEFINED, mCodeSettings); 249 assertParser("Icon with code", ICON_SETTINGS + "|" + CODE_SETTINGS, 250 null, null, mSettingsIconId, mCodeSettings); 251 } 252 253 public void testResourceReference() { 254 assertParser("Settings as more key", "!text/keyspec_settings", 255 null, null, mSettingsIconId, mCodeSettings); 256 257 assertParser("Action next as more key", "!text/label_next_key|!code/key_action_next", 258 "Next", null, ICON_UNDEFINED, mCodeActionNext); 259 260 assertParser("Popular domain", 261 "!text/keyspec_popular_domain|!text/keyspec_popular_domain ", 262 ".com", ".com ", ICON_UNDEFINED, CODE_OUTPUT_TEXT); 263 } 264 265 public void testFormatError() { 266 assertParserError("Empty label with outputText", "|a", 267 null, "a", ICON_UNDEFINED, CODE_UNSPECIFIED); 268 assertParserError("Empty label with code", "|" + CODE_SETTINGS, 269 null, null, ICON_UNDEFINED, mCodeSettings); 270 assertParserError("Empty outputText with label", "a|", 271 "a", null, ICON_UNDEFINED, CODE_UNSPECIFIED); 272 assertParserError("Empty outputText with icon", ICON_SETTINGS + "|", 273 null, null, mSettingsIconId, CODE_UNSPECIFIED); 274 assertParserError("Icon without code", ICON_SETTINGS, 275 null, null, mSettingsIconId, CODE_UNSPECIFIED); 276 assertParserError("Non existing icon", ICON_NON_EXISTING + "|abc", 277 null, "abc", ICON_UNDEFINED, CODE_OUTPUT_TEXT); 278 assertParserError("Non existing code", "abc|" + CODE_NON_EXISTING, 279 "abc", null, ICON_UNDEFINED, CODE_UNSPECIFIED); 280 assertParserError("Third bar at end", "a|b|", 281 "a", null, ICON_UNDEFINED, CODE_UNSPECIFIED); 282 assertParserError("Multiple bar", "a|b|c", 283 "a", null, ICON_UNDEFINED, CODE_UNSPECIFIED); 284 assertParserError("Multiple bar with label and code", "a|" + CODE_SETTINGS + "|c", 285 "a", null, ICON_UNDEFINED, mCodeSettings); 286 assertParserError("Multiple bar with icon and outputText", ICON_SETTINGS + "|b|c", 287 null, null, mSettingsIconId, CODE_UNSPECIFIED); 288 assertParserError("Multiple bar with icon and code", 289 ICON_SETTINGS + "|" + CODE_SETTINGS + "|c", 290 null, null, mSettingsIconId, mCodeSettings); 291 } 292 293 public void testUselessUpperCaseSpecifier() { 294 assertParser("Single letter with CODE", "a|" + CODE_SETTINGS_UPPERCASE, 295 "a", "!CODE/KEY_SETTINGS", ICON_UNDEFINED, CODE_OUTPUT_TEXT); 296 assertParser("Label with CODE", "abc|" + CODE_SETTINGS_UPPERCASE, 297 "abc", "!CODE/KEY_SETTINGS", ICON_UNDEFINED, CODE_OUTPUT_TEXT); 298 assertParser("Escaped label with CODE", "a\\|c|" + CODE_SETTINGS_UPPERCASE, 299 "a|c", "!CODE/KEY_SETTINGS", ICON_UNDEFINED, CODE_OUTPUT_TEXT); 300 assertParser("ICON with outputText", ICON_SETTINGS_UPPERCASE + "|abc", 301 "!ICON/SETTINGS_KEY", "abc", ICON_UNDEFINED, CODE_OUTPUT_TEXT); 302 assertParser("ICON with outputText starts with bang", ICON_SETTINGS_UPPERCASE + "|!bc", 303 "!ICON/SETTINGS_KEY", "!bc", ICON_UNDEFINED, CODE_OUTPUT_TEXT); 304 assertParser("ICON with outputText contains bang", ICON_SETTINGS_UPPERCASE + "|a!c", 305 "!ICON/SETTINGS_KEY", "a!c", ICON_UNDEFINED, CODE_OUTPUT_TEXT); 306 assertParser("ICON with escaped bang outputText", ICON_SETTINGS_UPPERCASE + "|\\!bc", 307 "!ICON/SETTINGS_KEY", "!bc", ICON_UNDEFINED, CODE_OUTPUT_TEXT); 308 assertParser("Label starts with bang and CODE", "!bc|" + CODE_SETTINGS_UPPERCASE, 309 "!bc", "!CODE/KEY_SETTINGS", ICON_UNDEFINED, CODE_OUTPUT_TEXT); 310 assertParser("Label contains bang and CODE", "a!c|" + CODE_SETTINGS_UPPERCASE, 311 "a!c", "!CODE/KEY_SETTINGS", ICON_UNDEFINED, CODE_OUTPUT_TEXT); 312 assertParser("Escaped bang label with CODE", "\\!bc|" + CODE_SETTINGS_UPPERCASE, 313 "!bc", "!CODE/KEY_SETTINGS", ICON_UNDEFINED, CODE_OUTPUT_TEXT); 314 assertParser("ICON with CODE", ICON_SETTINGS_UPPERCASE + "|" + CODE_SETTINGS_UPPERCASE, 315 "!ICON/SETTINGS_KEY", "!CODE/KEY_SETTINGS", ICON_UNDEFINED, CODE_OUTPUT_TEXT); 316 assertParser("SETTINGS AS MORE KEY", "!TEXT/SETTINGS_AS_MORE_KEY", 317 "!TEXT/SETTINGS_AS_MORE_KEY", "!TEXT/SETTINGS_AS_MORE_KEY", ICON_UNDEFINED, 318 CODE_OUTPUT_TEXT); 319 assertParser("ACTION NEXT AS MORE KEY", "!TEXT/LABEL_NEXT_KEY|!CODE/KEY_ACTION_NEXT", 320 "!TEXT/LABEL_NEXT_KEY", "!CODE/KEY_ACTION_NEXT", ICON_UNDEFINED, 321 CODE_OUTPUT_TEXT); 322 assertParser("POPULAR DOMAIN", 323 "!TEXT/KEYLABEL_FOR_POPULAR_DOMAIN|!TEXT/KEYLABEL_FOR_POPULAR_DOMAIN ", 324 "!TEXT/KEYLABEL_FOR_POPULAR_DOMAIN", "!TEXT/KEYLABEL_FOR_POPULAR_DOMAIN ", 325 ICON_UNDEFINED, CODE_OUTPUT_TEXT); 326 assertParserError("Empty label with CODE", "|" + CODE_SETTINGS_UPPERCASE, 327 null, null, ICON_UNDEFINED, mCodeSettings); 328 assertParserError("Empty outputText with ICON", ICON_SETTINGS_UPPERCASE + "|", 329 null, null, mSettingsIconId, CODE_UNSPECIFIED); 330 assertParser("ICON without code", ICON_SETTINGS_UPPERCASE, 331 "!ICON/SETTINGS_KEY", "!ICON/SETTINGS_KEY", ICON_UNDEFINED, CODE_OUTPUT_TEXT); 332 assertParserError("Multiple bar with label and CODE", "a|" + CODE_SETTINGS_UPPERCASE + "|c", 333 "a", null, ICON_UNDEFINED, mCodeSettings); 334 assertParserError("Multiple bar with ICON and outputText", ICON_SETTINGS_UPPERCASE + "|b|c", 335 null, null, mSettingsIconId, CODE_UNSPECIFIED); 336 assertParserError("Multiple bar with ICON and CODE", 337 ICON_SETTINGS_UPPERCASE + "|" + CODE_SETTINGS_UPPERCASE + "|c", 338 null, null, mSettingsIconId, mCodeSettings); 339 } 340 } 341