aboutsummaryrefslogtreecommitdiffstats
path: root/tests/src
diff options
context:
space:
mode:
Diffstat (limited to 'tests/src')
-rw-r--r--tests/src/com/android/inputmethod/keyboard/KeyboardLayoutSetActionLabelTests.java167
-rw-r--r--tests/src/com/android/inputmethod/keyboard/KeyboardLayoutSetSubtypesCountTests.java60
-rw-r--r--tests/src/com/android/inputmethod/keyboard/KeyboardLayoutSetTestsBase.java36
-rw-r--r--tests/src/com/android/inputmethod/keyboard/layout/AlphabetShifted.java46
-rw-r--r--tests/src/com/android/inputmethod/keyboard/layout/Qwerty.java46
-rw-r--r--tests/src/com/android/inputmethod/keyboard/layout/Symbols.java187
-rw-r--r--tests/src/com/android/inputmethod/keyboard/layout/SymbolsShifted.java142
-rw-r--r--tests/src/com/android/inputmethod/keyboard/layout/expected/AbstractKeyboardBuilder.java134
-rw-r--r--tests/src/com/android/inputmethod/keyboard/layout/expected/ActualKeyboardBuilder.java189
-rw-r--r--tests/src/com/android/inputmethod/keyboard/layout/expected/ExpectedKey.java186
-rw-r--r--tests/src/com/android/inputmethod/keyboard/layout/expected/ExpectedKeyOutput.java138
-rw-r--r--tests/src/com/android/inputmethod/keyboard/layout/expected/ExpectedKeyVisual.java135
-rw-r--r--tests/src/com/android/inputmethod/keyboard/layout/expected/ExpectedKeyboardBuilder.java272
-rw-r--r--tests/src/com/android/inputmethod/keyboard/layout/expected/LayoutBase.java143
-rw-r--r--tests/src/com/android/inputmethod/keyboard/layout/tests/LayoutTestsBase.java197
-rw-r--r--tests/src/com/android/inputmethod/keyboard/layout/tests/TestsEnglishUS.java98
16 files changed, 2155 insertions, 21 deletions
diff --git a/tests/src/com/android/inputmethod/keyboard/KeyboardLayoutSetActionLabelTests.java b/tests/src/com/android/inputmethod/keyboard/KeyboardLayoutSetActionLabelTests.java
new file mode 100644
index 000000000..06139b808
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/KeyboardLayoutSetActionLabelTests.java
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard;
+
+import android.content.res.Resources;
+import android.test.suitebuilder.annotation.MediumTest;
+import android.text.InputType;
+import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.InputMethodSubtype;
+
+import com.android.inputmethod.keyboard.internal.KeyboardIconsSet;
+import com.android.inputmethod.latin.Constants;
+import com.android.inputmethod.latin.R;
+import com.android.inputmethod.latin.utils.RunInLocale;
+import com.android.inputmethod.latin.utils.SubtypeLocaleUtils;
+
+@MediumTest
+public final class KeyboardLayoutSetActionLabelTests extends KeyboardLayoutSetTestsBase {
+ private static void doTestActionKey(final String tag, final KeyboardLayoutSet layoutSet,
+ final int elementId, final String label, final int iconId) {
+ final Keyboard keyboard = layoutSet.getKeyboard(elementId);
+ final Key enterKey = keyboard.getKey(Constants.CODE_ENTER);
+ assertNotNull(tag + " enter key on " + keyboard.mId, enterKey);
+ assertEquals(tag + " enter label " + enterKey, label, enterKey.getLabel());
+ assertEquals(tag + " enter icon " + enterKey, iconId, enterKey.getIconId());
+ }
+
+ private void doTestActionLabel(final String tag, final InputMethodSubtype subtype,
+ final int actionId, final int labelResId) {
+ final EditorInfo editorInfo = new EditorInfo();
+ editorInfo.imeOptions = actionId;
+ final RunInLocale<String> job = new RunInLocale<String>() {
+ @Override
+ protected String job(final Resources res) {
+ return res.getString(labelResId);
+ }
+ };
+ final Resources res = getContext().getResources();
+ final String label;
+ if (subtype.getLocale().equals(SubtypeLocaleUtils.NO_LANGUAGE)) {
+ // Using system locale.
+ label = res.getString(labelResId);
+ } else {
+ label = job.runInLocale(res, SubtypeLocaleUtils.getSubtypeLocale(subtype));
+ }
+ // Test text layouts.
+ editorInfo.inputType = InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_NORMAL;
+ final KeyboardLayoutSet layoutSet = createKeyboardLayoutSet(subtype, editorInfo);
+ doTestActionKey(tag, layoutSet, KeyboardId.ELEMENT_ALPHABET,
+ label, KeyboardIconsSet.ICON_UNDEFINED);
+ doTestActionKey(tag, layoutSet, KeyboardId.ELEMENT_SYMBOLS,
+ label, KeyboardIconsSet.ICON_UNDEFINED);
+ doTestActionKey(tag, layoutSet, KeyboardId.ELEMENT_SYMBOLS_SHIFTED,
+ label, KeyboardIconsSet.ICON_UNDEFINED);
+ // Test phone number layouts.
+ doTestActionKey(tag, layoutSet, KeyboardId.ELEMENT_PHONE,
+ label, KeyboardIconsSet.ICON_UNDEFINED);
+ doTestActionKey(tag, layoutSet, KeyboardId.ELEMENT_PHONE_SYMBOLS,
+ label, KeyboardIconsSet.ICON_UNDEFINED);
+ // Test normal number layout.
+ doTestActionKey(tag, layoutSet, KeyboardId.ELEMENT_NUMBER,
+ label, KeyboardIconsSet.ICON_UNDEFINED);
+ // Test number password layouts.
+ editorInfo.inputType =
+ InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_VARIATION_PASSWORD;
+ final KeyboardLayoutSet passwordSet = createKeyboardLayoutSet(subtype, editorInfo);
+ doTestActionKey(tag, passwordSet, KeyboardId.ELEMENT_NUMBER,
+ label, KeyboardIconsSet.ICON_UNDEFINED);
+ }
+
+ private void doTestActionKeyIcon(final String tag, final InputMethodSubtype subtype,
+ final int actionId, final String iconName) {
+ final int iconId = KeyboardIconsSet.getIconId(iconName);
+ final EditorInfo editorInfo = new EditorInfo();
+ editorInfo.imeOptions = actionId;
+ // Test text layouts.
+ editorInfo.inputType = InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_NORMAL;
+ final KeyboardLayoutSet layoutSet = createKeyboardLayoutSet(subtype, editorInfo);
+ doTestActionKey(tag, layoutSet, KeyboardId.ELEMENT_ALPHABET, null /* label */, iconId);
+ doTestActionKey(tag, layoutSet, KeyboardId.ELEMENT_SYMBOLS, null /* label */, iconId);
+ doTestActionKey(
+ tag, layoutSet, KeyboardId.ELEMENT_SYMBOLS_SHIFTED, null /* label */, iconId);
+ // Test phone number layouts.
+ doTestActionKey(tag, layoutSet, KeyboardId.ELEMENT_PHONE, null /* label */, iconId);
+ doTestActionKey(
+ tag, layoutSet, KeyboardId.ELEMENT_PHONE_SYMBOLS, null /* label */, iconId);
+ // Test normal number layout.
+ doTestActionKey(tag, layoutSet, KeyboardId.ELEMENT_NUMBER, null /* label */, iconId);
+ // Test number password layout.
+ editorInfo.inputType =
+ InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_VARIATION_PASSWORD;
+ final KeyboardLayoutSet passwordSet = createKeyboardLayoutSet(subtype, editorInfo);
+ doTestActionKey(tag, passwordSet, KeyboardId.ELEMENT_NUMBER, null /* label */, iconId);
+ }
+
+ public void testActionUnspecified() {
+ for (final InputMethodSubtype subtype : getAllSubtypesList()) {
+ final String tag = "unspecifiled "
+ + SubtypeLocaleUtils.getSubtypeNameForLogging(subtype);
+ doTestActionKeyIcon(tag, subtype, EditorInfo.IME_ACTION_UNSPECIFIED, "enter_key");
+ }
+ }
+
+ public void testActionNone() {
+ for (final InputMethodSubtype subtype : getAllSubtypesList()) {
+ final String tag = "none " + SubtypeLocaleUtils.getSubtypeNameForLogging(subtype);
+ doTestActionKeyIcon(tag, subtype, EditorInfo.IME_ACTION_NONE, "enter_key");
+ }
+ }
+
+ public void testActionGo() {
+ for (final InputMethodSubtype subtype : getAllSubtypesList()) {
+ final String tag = "go " + SubtypeLocaleUtils.getSubtypeNameForLogging(subtype);
+ doTestActionLabel(tag, subtype, EditorInfo.IME_ACTION_GO, R.string.label_go_key);
+ }
+ }
+
+ public void testActionSearch() {
+ for (final InputMethodSubtype subtype : getAllSubtypesList()) {
+ final String tag = "search " + SubtypeLocaleUtils.getSubtypeNameForLogging(subtype);
+ doTestActionKeyIcon(tag, subtype, EditorInfo.IME_ACTION_SEARCH, "search_key");
+ }
+ }
+
+ public void testActionSend() {
+ for (final InputMethodSubtype subtype : getAllSubtypesList()) {
+ final String tag = "send " + SubtypeLocaleUtils.getSubtypeNameForLogging(subtype);
+ doTestActionLabel(tag, subtype, EditorInfo.IME_ACTION_SEND, R.string.label_send_key);
+ }
+ }
+
+ public void testActionNext() {
+ for (final InputMethodSubtype subtype : getAllSubtypesList()) {
+ final String tag = "next " + SubtypeLocaleUtils.getSubtypeNameForLogging(subtype);
+ doTestActionLabel(tag, subtype, EditorInfo.IME_ACTION_NEXT, R.string.label_next_key);
+ }
+ }
+
+ public void testActionDone() {
+ for (final InputMethodSubtype subtype : getAllSubtypesList()) {
+ final String tag = "done " + SubtypeLocaleUtils.getSubtypeNameForLogging(subtype);
+ doTestActionLabel(tag, subtype, EditorInfo.IME_ACTION_DONE, R.string.label_done_key);
+ }
+ }
+
+ public void testActionPrevious() {
+ for (final InputMethodSubtype subtype : getAllSubtypesList()) {
+ final String tag = "previous " + SubtypeLocaleUtils.getSubtypeNameForLogging(subtype);
+ doTestActionLabel(
+ tag, subtype, EditorInfo.IME_ACTION_PREVIOUS, R.string.label_previous_key);
+ }
+ }
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/KeyboardLayoutSetSubtypesCountTests.java b/tests/src/com/android/inputmethod/keyboard/KeyboardLayoutSetSubtypesCountTests.java
new file mode 100644
index 000000000..e691639a8
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/KeyboardLayoutSetSubtypesCountTests.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard;
+
+import android.test.suitebuilder.annotation.SmallTest;
+import android.view.inputmethod.InputMethodSubtype;
+
+import com.android.inputmethod.latin.utils.SubtypeLocaleUtils;
+
+import java.util.ArrayList;
+
+@SmallTest
+public class KeyboardLayoutSetSubtypesCountTests extends KeyboardLayoutSetTestsBase {
+ private static final int NUMBER_OF_SUBTYPES = 63;
+ private static final int NUMBER_OF_ASCII_CAPABLE_SUBTYPES = 40;
+ private static final int NUMBER_OF_PREDEFINED_ADDITIONAL_SUBTYPES = 2;
+
+ private static String toString(final ArrayList<InputMethodSubtype> subtypeList) {
+ final StringBuilder sb = new StringBuilder();
+ for (int index = 0; index < subtypeList.size(); index++) {
+ final InputMethodSubtype subtype = subtypeList.get(index);
+ sb.append(index + ": ");
+ sb.append(SubtypeLocaleUtils.getSubtypeNameForLogging(subtype));
+ sb.append("\n");
+ }
+ return sb.toString();
+ }
+
+ public final void testAllSubtypesCount() {
+ final ArrayList<InputMethodSubtype> allSubtypesList = getAllSubtypesList();
+ assertEquals(toString(allSubtypesList), NUMBER_OF_SUBTYPES, allSubtypesList.size());
+ }
+
+ public final void testAsciiCapableSubtypesCount() {
+ final ArrayList<InputMethodSubtype> asciiCapableSubtypesList =
+ getAsciiCapableSubtypesList();
+ assertEquals(toString(asciiCapableSubtypesList),
+ NUMBER_OF_ASCII_CAPABLE_SUBTYPES, asciiCapableSubtypesList.size());
+ }
+
+ public final void testAdditionalSubtypesCount() {
+ final ArrayList<InputMethodSubtype> additionalSubtypesList = getAdditionalSubtypesList();
+ assertEquals(toString(additionalSubtypesList),
+ NUMBER_OF_PREDEFINED_ADDITIONAL_SUBTYPES, additionalSubtypesList.size());
+ }
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/KeyboardLayoutSetTestsBase.java b/tests/src/com/android/inputmethod/keyboard/KeyboardLayoutSetTestsBase.java
index 9939a4335..0993c4b67 100644
--- a/tests/src/com/android/inputmethod/keyboard/KeyboardLayoutSetTestsBase.java
+++ b/tests/src/com/android/inputmethod/keyboard/KeyboardLayoutSetTestsBase.java
@@ -41,9 +41,6 @@ import java.util.Locale;
@SmallTest
public class KeyboardLayoutSetTestsBase extends AndroidTestCase {
- private static final int NUMBER_OF_SUBTYPES = 63;
- private static final int NUMBER_OF_ASCII_CAPABLE_SUBTYPES = 40;
-
private static final KeyboardTheme DEFAULT_KEYBOARD_THEME =
KeyboardSwitcher.KEYBOARD_THEMES[KeyboardSwitcher.THEME_INDEX_DEFAULT];
@@ -51,6 +48,8 @@ public class KeyboardLayoutSetTestsBase extends AndroidTestCase {
private final ArrayList<InputMethodSubtype> mAllSubtypesList = CollectionUtils.newArrayList();
private final ArrayList<InputMethodSubtype> mAsciiCapableSubtypesList =
CollectionUtils.newArrayList();
+ private final ArrayList<InputMethodSubtype> mAdditionalSubtypesList =
+ CollectionUtils.newArrayList();
private Context mThemeContext;
private int mScreenMetrics;
@@ -68,6 +67,10 @@ public class KeyboardLayoutSetTestsBase extends AndroidTestCase {
final int subtypeCount = imi.getSubtypeCount();
for (int index = 0; index < subtypeCount; index++) {
final InputMethodSubtype subtype = imi.getSubtypeAt(index);
+ if (AdditionalSubtypeUtils.isAdditionalSubtype(subtype)) {
+ mAdditionalSubtypesList.add(subtype);
+ continue;
+ }
mAllSubtypesList.add(subtype);
if (InputMethodSubtypeCompatUtils.isAsciiCapable(subtype)) {
mAsciiCapableSubtypesList.add(subtype);
@@ -75,30 +78,21 @@ public class KeyboardLayoutSetTestsBase extends AndroidTestCase {
}
}
- protected final boolean isPhone() {
- return mScreenMetrics == Constants.SCREEN_METRICS_SMALL_PHONE
- || mScreenMetrics == Constants.SCREEN_METRICS_LARGE_PHONE;
+ protected final ArrayList<InputMethodSubtype> getAllSubtypesList() {
+ return mAllSubtypesList;
}
- private static String toString(final ArrayList<InputMethodSubtype> subtypeList) {
- final StringBuilder sb = new StringBuilder();
- for (int index = 0; index < subtypeList.size(); index++) {
- final InputMethodSubtype subtype = subtypeList.get(index);
- sb.append((index + 1) + ": ");
- sb.append(SubtypeLocaleUtils.getSubtypeNameForLogging(subtype));
- sb.append("\n");
- }
- return sb.toString();
+ protected final ArrayList<InputMethodSubtype> getAsciiCapableSubtypesList() {
+ return mAsciiCapableSubtypesList;
}
- public final void testAllSubtypesCount() {
- assertEquals(toString(mAllSubtypesList),
- NUMBER_OF_SUBTYPES, mAllSubtypesList.size());
+ protected final ArrayList<InputMethodSubtype> getAdditionalSubtypesList() {
+ return mAdditionalSubtypesList;
}
- public final void testAsciiCapableSubtypesCount() {
- assertEquals(toString(mAsciiCapableSubtypesList),
- NUMBER_OF_ASCII_CAPABLE_SUBTYPES, mAsciiCapableSubtypesList.size());
+ protected final boolean isPhone() {
+ return mScreenMetrics == Constants.SCREEN_METRICS_SMALL_PHONE
+ || mScreenMetrics == Constants.SCREEN_METRICS_LARGE_PHONE;
}
protected final InputMethodSubtype getSubtype(final Locale locale,
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/AlphabetShifted.java b/tests/src/com/android/inputmethod/keyboard/layout/AlphabetShifted.java
new file mode 100644
index 000000000..be3ed12de
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/AlphabetShifted.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout;
+
+import com.android.inputmethod.keyboard.internal.KeyboardIconsSet;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
+import com.android.inputmethod.keyboard.layout.expected.LayoutBase;
+import com.android.inputmethod.latin.Constants;
+
+import java.util.Locale;
+
+/**
+ * The generic upper case alphabet keyboard layout.
+ */
+public final class AlphabetShifted extends LayoutBase {
+ public static ExpectedKey[][] getAlphabet(final ExpectedKey[][] lowerCaseKeyboard,
+ final Locale locale) {
+ final ExpectedKey[][] upperCaseKeyboard = ExpectedKeyboardBuilder.toUpperCase(
+ lowerCaseKeyboard, locale);
+ return new ExpectedKeyboardBuilder(upperCaseKeyboard)
+ .replaceKeyOfAll(SHIFT_KEY, SHIFTED_SHIFT_KEY)
+ .build();
+ }
+
+ // Icon id.
+ private static final int ICON_SHIFTED_SHIFT = KeyboardIconsSet.getIconId("shift_key_shifted");
+
+ // Functional key.
+ private static final ExpectedKey SHIFTED_SHIFT_KEY = key(
+ ICON_SHIFTED_SHIFT, Constants.CODE_SHIFT, CAPSLOCK_MORE_KEY);
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/Qwerty.java b/tests/src/com/android/inputmethod/keyboard/layout/Qwerty.java
new file mode 100644
index 000000000..f7179b739
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/Qwerty.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout;
+
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
+import com.android.inputmethod.keyboard.layout.expected.LayoutBase;
+
+/**
+ * The QWERTY alphabet keyboard.
+ */
+public final class Qwerty extends LayoutBase {
+ public static ExpectedKey[][] getAlphabet(final boolean isPhone) {
+ return toCommonAlphabet(ALPHABET_COMMON, isPhone);
+ }
+
+ private static final ExpectedKey[][] ALPHABET_COMMON = new ExpectedKeyboardBuilder(10, 9, 7, 3)
+ .setLabelsOfRow(1, "q", "w", "e", "r", "t", "y", "u", "i", "o", "p")
+ .setMoreKeysOf("q", "1")
+ .setMoreKeysOf("w", "2")
+ .setMoreKeysOf("e", "3")
+ .setMoreKeysOf("r", "4")
+ .setMoreKeysOf("t", "5")
+ .setMoreKeysOf("y", "6")
+ .setMoreKeysOf("u", "7")
+ .setMoreKeysOf("i", "8")
+ .setMoreKeysOf("o", "9")
+ .setMoreKeysOf("p", "0")
+ .setLabelsOfRow(2, "a", "s", "d", "f", "g", "h", "j", "k", "l")
+ .setLabelsOfRow(3, "z", "x", "c", "v", "b", "n", "m")
+ .build();
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/Symbols.java b/tests/src/com/android/inputmethod/keyboard/layout/Symbols.java
new file mode 100644
index 000000000..03d7f07e9
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/Symbols.java
@@ -0,0 +1,187 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout;
+
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
+import com.android.inputmethod.keyboard.layout.expected.LayoutBase;
+import com.android.inputmethod.latin.Constants;
+
+/**
+ * The symbols keyboard layout.
+ */
+public final class Symbols extends LayoutBase {
+ public static ExpectedKey[][] getSymbols(final boolean isPhone) {
+ return isPhone ? toPhoneSymbol(SYMBOLS_COMMON) : toTabletSymbols(SYMBOLS_COMMON);
+ }
+
+ // Functional keys.
+ public static final ExpectedKey ALPHABET_KEY = key("ABC", Constants.CODE_SWITCH_ALPHA_SYMBOL);
+ public static final ExpectedKey SYMBOLS_SHIFT_KEY = key("= \\ <", Constants.CODE_SHIFT);
+ public static final ExpectedKey TABLET_SYMBOLS_SHIFT_KEY = key("~ [ <", Constants.CODE_SHIFT);
+
+ // Common symbols keyboard layout.
+ public static final ExpectedKey[][] SYMBOLS_COMMON = new ExpectedKeyboardBuilder(10, 9, 7, 5)
+ .setLabelsOfRow(1, "1", "2", "3", "4", "5", "6", "7", "8", "9", "0")
+ // U+00B9: "¹" SUPERSCRIPT ONE
+ // U+00BD: "½" VULGAR FRACTION ONE HALF
+ // U+2153: "⅓" VULGAR FRACTION ONE THIRD
+ // U+00BC: "¼" VULGAR FRACTION ONE QUARTER
+ // U+215B: "⅛" VULGAR FRACTION ONE EIGHTH
+ .setMoreKeysOf("1", "\u00B9", "\u00BD", "\u2153", "\u00BC", "\u215B")
+ // U+00B2: "²" SUPERSCRIPT TWO
+ // U+2154: "⅔" VULGAR FRACTION TWO THIRDS
+ .setMoreKeysOf("2", "\u00B2", "\u2154")
+ // U+00B3: "³" SUPERSCRIPT THREE
+ // U+00BE: "¾" VULGAR FRACTION THREE QUARTERS
+ // U+215C: "⅜" VULGAR FRACTION THREE EIGHTHS
+ .setMoreKeysOf("3", "\u00B3", "\u00BE", "\u215C")
+ // U+2074: "⁴" SUPERSCRIPT FOUR
+ .setMoreKeysOf("4", "\u2074")
+ // U+215D: "⅝" VULGAR FRACTION FIVE EIGHTHS
+ .setMoreKeysOf("5", "\u215D")
+ // U+215E: "⅞" VULGAR FRACTION SEVEN EIGHTHS
+ .setMoreKeysOf("7", "\u215E")
+ // U+207F: "ⁿ" SUPERSCRIPT LATIN SMALL LETTER N
+ // U+2205: "∅" EMPTY SET
+ .setMoreKeysOf("0", "\u207F", "\u2205")
+ .setLabelsOfRow(2, "@", "#", "$", "%", "&", "-", "+", "(", ")")
+ // U+00A2: "¢" CENT SIGN
+ // U+00A3: "£" POUND SIGN
+ // U+20AC: "€" EURO SIGN
+ // U+00A5: "¥" YEN SIGN
+ // U+20B1: "₱" PESO SIGN
+ .setMoreKeysOf("$", "\u00A2", "\u00A3", "\u20AC", "\u00A5", "\u20B1")
+ // U+2030: "‰" PER MILLE SIGN
+ .setMoreKeysOf("%", "\u2030")
+ // U+2013: "–" EN DASH
+ // U+2014: "—" EM DASH
+ // U+00B7: "·" MIDDLE DOT
+ .setMoreKeysOf("-", "_", "\u2013", "\u2014", "\u00B7")
+ // U+00B1: "±" PLUS-MINUS SIGN
+ .setMoreKeysOf("+", "\u00B1")
+ .setMoreKeysOf("(", "<", "{", "[")
+ .setMoreKeysOf(")", ">", "}", "]")
+ .setLabelsOfRow(3, "*", "\"", "'", ":", ";", "!", "?")
+ // U+2020: "†" DAGGER
+ // U+2021: "‡" DOUBLE DAGGER
+ // U+2605: "★" BLACK STAR
+ .setMoreKeysOf("*", "\u2020", "\u2021", "\u2605")
+ // U+201E: "„" DOUBLE LOW-9 QUOTATION MARK
+ // U+201C: "“" LEFT DOUBLE QUOTATION MARK
+ // U+201D: "”" RIGHT DOUBLE QUOTATION MARK
+ // U+00AB: "«" LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
+ // U+00BB: "»" RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
+ .setMoreKeysOf("\"", "\u201E", "\u201C", "\u201D", "\u00AB", "\u00BB")
+ // U+201A: "‚" SINGLE LOW-9 QUOTATION MARK
+ // U+2018: "‘" LEFT SINGLE QUOTATION MARK
+ // U+2019: "’" RIGHT SINGLE QUOTATION MARK
+ // U+2039: "‹" SINGLE LEFT-POINTING ANGLE QUOTATION MARK
+ // U+203A: "›" SINGLE RIGHT-POINTING ANGLE QUOTATION MARK
+ .setMoreKeysOf("'", "\u201A", "\u2018", "\u2019", "\u2039", "\u203A")
+ // U+00A1: "¡" INVERTED EXCLAMATION MARK
+ .setMoreKeysOf("!", "\u00A1")
+ // U+00BF: "¿" INVERTED QUESTION MARK
+ .setMoreKeysOf("?", "\u00BF")
+ .setLabelsOfRow(4, "_", "/", " ", ",", ".")
+ // U+2026: "…" HORIZONTAL ELLIPSIS
+ .setMoreKeysOf(".", "\u2026")
+ .build();
+
+ private static ExpectedKey[][] toPhoneSymbol(final ExpectedKey[][] common) {
+ return new ExpectedKeyboardBuilder(common)
+ .addKeysOnTheLeftOfRow(3, Symbols.SYMBOLS_SHIFT_KEY)
+ .addKeysOnTheRightOfRow(3, DELETE_KEY)
+ .addKeysOnTheLeftOfRow(4, Symbols.ALPHABET_KEY)
+ .addKeysOnTheRightOfRow(4, key(ENTER_KEY, EMOJI_KEY))
+ .build();
+ }
+
+ private static ExpectedKey[][] toTabletSymbols(final ExpectedKey[][] common) {
+ return new ExpectedKeyboardBuilder(common)
+ .addKeysOnTheLeftOfRow(3,
+ key("\\"), key("="))
+ .addKeysOnTheRightOfRow(1, DELETE_KEY)
+ .addKeysOnTheRightOfRow(2, ENTER_KEY)
+ .addKeysOnTheLeftOfRow(3, Symbols.TABLET_SYMBOLS_SHIFT_KEY)
+ .addKeysOnTheRightOfRow(3, Symbols.TABLET_SYMBOLS_SHIFT_KEY)
+ .addKeysOnTheLeftOfRow(4, Symbols.ALPHABET_KEY)
+ .addKeysOnTheRightOfRow(4, EMOJI_KEY)
+ .build();
+ }
+
+ // Helper method to add currency symbols for Euro.
+ public static ExpectedKeyboardBuilder euro(final ExpectedKeyboardBuilder builder) {
+ return builder
+ // U+20AC: "€" EURO SIGN
+ // U+00A2: "¢" CENT SIGN
+ // U+00A3: "£" POUND SIGN
+ // U+00A5: "¥" YEN SIGN
+ // U+20B1: "₱" PESO SIGN
+ .replaceKeyOfLabel("$", key("\u20AC",
+ moreKey("\u00A2"), moreKey("\u00A3"), moreKey("$"),
+ moreKey("\u00A5"), moreKey("\u20B1")));
+ }
+
+ // Helper method to add single quotes "more keys".
+ // "9LLR" means "9-low/Left quotation marks, Left/Right-pointing angle quotation marks".
+ public static ExpectedKeyboardBuilder singleQuotes9LLR(final ExpectedKeyboardBuilder builder) {
+ return builder
+ // U+2019: "’" RIGHT SINGLE QUOTATION MARK
+ // U+201A: "‚" SINGLE LOW-9 QUOTATION MARK
+ // U+2018: "‘" LEFT SINGLE QUOTATION MARK
+ // U+2039: "‹" SINGLE LEFT-POINTING ANGLE QUOTATION MARK
+ // U+203A: "›" SINGLE RIGHT-POINTING ANGLE QUOTATION MARK
+ .setMoreKeysOf("'", "\u2019", "\u201A", "\u2018", "\u2039", "\u203A");
+ }
+
+ // Helper method to add single quotes "more keys".
+ // "9LLR" means "9-low/Left quotation marks, Right/Left-pointing angle quotation marks".
+ public static ExpectedKeyboardBuilder singleQuotes9LRL(final ExpectedKeyboardBuilder builder) {
+ return builder
+ // U+2019: "’" RIGHT SINGLE QUOTATION MARK
+ // U+201A: "‚" SINGLE LOW-9 QUOTATION MARK
+ // U+2018: "‘" LEFT SINGLE QUOTATION MARK
+ // U+203A: "›" SINGLE RIGHT-POINTING ANGLE QUOTATION MARK
+ // U+2039: "‹" SINGLE LEFT-POINTING ANGLE QUOTATION MARK
+ .setMoreKeysOf("'", "\u2019", "\u201A", "\u2018", "\u203A", "\u2039");
+ }
+
+ // Helper method to add double quotes "more keys".
+ // "9LLR" means "9-low/Left quotation marks, Left/Right-pointing angle quotation marks".
+ public static ExpectedKeyboardBuilder doubleQuotes9LLR(final ExpectedKeyboardBuilder builder) {
+ return builder
+ // U+201D: "”" RIGHT DOUBLE QUOTATION MARK
+ // U+201E: "„" DOUBLE LOW-9 QUOTATION MARK
+ // U+201C: "“" LEFT DOUBLE QUOTATION MARK
+ // U+00AB: "«" LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
+ // U+00BB: "»" RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
+ .setMoreKeysOf("\"", "\u201D", "\u201E", "\u201C", "\u00AB", "\u00BB");
+ }
+
+ // Helper method to add double quotes "more keys".
+ // "9LLR" means "9-low/Left quotation marks, Right/Left-pointing angle quotation marks".
+ public static ExpectedKeyboardBuilder doubleQuotes9LRL(final ExpectedKeyboardBuilder builder) {
+ return builder
+ // U+201D: "”" RIGHT DOUBLE QUOTATION MARK
+ // U+201E: "„" DOUBLE LOW-9 QUOTATION MARK
+ // U+201C: "“" LEFT DOUBLE QUOTATION MARK
+ // U+00BB: "»" RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
+ // U+00AB: "«" LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
+ .setMoreKeysOf("\"", "\u201D", "\u201E", "\u201C", "\u00BB", "\u00AB");
+ }
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/SymbolsShifted.java b/tests/src/com/android/inputmethod/keyboard/layout/SymbolsShifted.java
new file mode 100644
index 000000000..368f9db46
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/SymbolsShifted.java
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout;
+
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
+import com.android.inputmethod.keyboard.layout.expected.LayoutBase;
+import com.android.inputmethod.latin.Constants;
+
+/**
+ * The symbols shifted keyboard layout.
+ */
+public final class SymbolsShifted extends LayoutBase {
+ public static ExpectedKey[][] getSymbolsShifted(final boolean isPhone) {
+ return isPhone ? toPhoneSymbolsShifted(SYMBOLS_SHIFTED_COMMON)
+ : toTabletSymbolsShifted(SYMBOLS_SHIFTED_COMMON);
+ }
+
+ // Functional key.
+ public static final ExpectedKey BACK_TO_SYMBOLS_KEY = key("?123", Constants.CODE_SHIFT);
+
+ // Common symbols shifted keyboard layout.
+ public static final ExpectedKey[][] SYMBOLS_SHIFTED_COMMON =
+ new ExpectedKeyboardBuilder(10, 9, 7, 5)
+ // U+0060: "`" GRAVE ACCENT
+ // U+2022: "•" BULLET
+ // U+221A: "√" SQUARE ROOT
+ // U+03C0: "π" GREEK SMALL LETTER PI
+ // U+00F7: "÷" DIVISION SIGN
+ // U+00D7: "×" MULTIPLICATION SIGN
+ // U+00B6: "¶" PILCROW SIGN
+ // U+2206: "∆" INCREMENT
+ .setLabelsOfRow(1,
+ "~", "\u0060", "|", "\u2022", "\u221A",
+ "\u03C0", "\u00F7", "\u00D7", "\u00B6", "\u2206")
+ // U+2022: "•" BULLET
+ // U+266A: "♪" EIGHTH NOTE
+ // U+2665: "♥" BLACK HEART SUIT
+ // U+2660: "♠" BLACK SPADE SUIT
+ // U+2666: "♦" BLACK DIAMOND SUIT
+ // U+2663: "♣" BLACK CLUB SUIT
+ .setMoreKeysOf("\u2022", "\u266A", "\u2665", "\u2660", "\u2666", "\u2663")
+ // U+03C0: "π" GREEK SMALL LETTER PI
+ // U+03A0: "Π" GREEK CAPITAL LETTER PI
+ .setMoreKeysOf("\u03C0", "\u03A0")
+ // U+00B6: "¶" PILCROW SIGN
+ // U+00A7: "§" SECTION SIGN
+ .setMoreKeysOf("\u00B6", "\u00A7")
+ // U+00A3: "£" POUND SIGN
+ // U+00A2: "¢" CENT SIGN
+ // U+20AC: "€" EURO SIGN
+ // U+00A5: "¥" YEN SIGN
+ // U+00B0: "°" DEGREE SIGN
+ .setLabelsOfRow(2,
+ "\u00A3", "\u00A2", "\u20AC", "\u00A5", "^",
+ "\u00B0", "=", "{", "}")
+ // U+2191: "↑" UPWARDS ARROW
+ // U+2193: "↓" DOWNWARDS ARROW
+ // U+2190: "←" LEFTWARDS ARROW
+ // U+2192: "→" RIGHTWARDS ARROW
+ .setMoreKeysOf("^", "\u2191", "\u2193", "\u2190", "\u2192")
+ // U+00B0: "°" DEGREE SIGN
+ // U+2032: "′" PRIME
+ // U+2033: "″" DOUBLE PRIME
+ .setMoreKeysOf("\u00B0", "\u2032", "\u2033")
+ // U+2260: "≠" NOT EQUAL TO
+ // U+2248: "≈" ALMOST EQUAL TO
+ // U+221E: "∞" INFINITY
+ .setMoreKeysOf("=", "\u2260", "\u2248", "\u221E")
+ // U+00A9: "©" COPYRIGHT SIGN
+ // U+00AE: "®" REGISTERED SIGN
+ // U+2122: "™" TRADE MARK SIGN
+ // U+2105: "℅" CARE OF
+ .setLabelsOfRow(3,
+ "\\", "\u00A9", "\u00AE", "\u2122", "\u2105",
+ "[", "]")
+ .setLabelsOfRow(4,
+ "<", ">", " ", ",", ".")
+ // U+2039: "‹" SINGLE LEFT-POINTING ANGLE QUOTATION MARK
+ // U+2264: "≤" LESS-THAN OR EQUAL TO
+ // U+00AB: "«" LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
+ .setMoreKeysOf("<", "\u2039", "\u2264", "\u00AB")
+ // U+203A: "›" SINGLE RIGHT-POINTING ANGLE QUOTATION MARK
+ // U+2265: "≥" GREATER-THAN EQUAL TO
+ // U+00BB: "»" RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
+ .setMoreKeysOf(">", "\u203A", "\u2265", "\u00BB")
+ // U+2026: "…" HORIZONTAL ELLIPSIS
+ .setMoreKeysOf(".", "\u2026")
+ .build();
+
+ private static ExpectedKey[][] toPhoneSymbolsShifted(final ExpectedKey[][] common) {
+ return new ExpectedKeyboardBuilder(common)
+ .addKeysOnTheLeftOfRow(3, BACK_TO_SYMBOLS_KEY)
+ .addKeysOnTheRightOfRow(3, DELETE_KEY)
+ .addKeysOnTheLeftOfRow(4, Symbols.ALPHABET_KEY)
+ .addKeysOnTheRightOfRow(4, key(ENTER_KEY, EMOJI_KEY))
+ .build();
+ }
+
+ private static ExpectedKey[][] toTabletSymbolsShifted(final ExpectedKey[][] common) {
+ return new ExpectedKeyboardBuilder(common)
+ // U+00BF: "¿" INVERTED QUESTION MARK
+ // U+00A1: "¡" INVERTED EXCLAMATION MARK
+ .addKeysOnTheRightOfRow(3,
+ key("\u00A1"), key("\u00BF"))
+ .addKeysOnTheRightOfRow(1, DELETE_KEY)
+ .addKeysOnTheRightOfRow(2, ENTER_KEY)
+ .addKeysOnTheLeftOfRow(3, BACK_TO_SYMBOLS_KEY)
+ .addKeysOnTheRightOfRow(3, BACK_TO_SYMBOLS_KEY)
+ .addKeysOnTheLeftOfRow(4, Symbols.ALPHABET_KEY)
+ .addKeysOnTheRightOfRow(4, EMOJI_KEY)
+ .build();
+ }
+
+ // Helper method to add currency symbols for Euro.
+ public static ExpectedKeyboardBuilder euro(final ExpectedKeyboardBuilder builder) {
+ return builder
+ // U+00A5: "¥" YEN SIGN
+ // U+00A2: "¢" CENT SIGN
+ .replaceKeyOfLabel("\u00A5", key("\u00A2"))
+ // U+20AC: "€" EURO SIGN
+ // U+00A2: "¢" CENT SIGN
+ .replaceKeyOfLabel("\u20AC", key("$", moreKey("\u00A2")))
+ // U+00A2: "¢" CENT SIGN
+ // U+00A5: "¥" YEN SIGN
+ .replaceKeyOfLabel("\u00A2", key("\u00A5"));
+ }
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/expected/AbstractKeyboardBuilder.java b/tests/src/com/android/inputmethod/keyboard/layout/expected/AbstractKeyboardBuilder.java
new file mode 100644
index 000000000..45449b762
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/expected/AbstractKeyboardBuilder.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout.expected;
+
+import java.util.Arrays;
+
+/**
+ * This class builds a keyboard that is a two dimensional array of elements <code>E</code>.
+ *
+ * A keyboard consists of array of rows, and a row consists of array of elements. Each row may have
+ * different number of elements. A element of a keyboard can be specified by a row number and a
+ * column number, both numbers starts from 1.
+ *
+ * @param <E> the type of a keyboard element. A keyboard element must be an immutable object.
+ */
+abstract class AbstractKeyboardBuilder<E> {
+ // A building array of rows.
+ private final E[][] mRows;
+
+ // Returns an instance of default element.
+ abstract E defaultElement();
+ // Returns an <code>E</code> array instance of the <code>size</code>.
+ abstract E[] newArray(final int size);
+ // Returns an <code>E[]</code> array instance of the <code>size</code>.
+ abstract E[][] newArrayOfArray(final int size);
+
+ /**
+ * Construct a builder filled with the default element.
+ * @param dimensions the integer array of each row's size.
+ */
+ AbstractKeyboardBuilder(final int ... dimensions) {
+ mRows = newArrayOfArray(dimensions.length);
+ for (int rowIndex = 0; rowIndex < dimensions.length; rowIndex++) {
+ mRows[rowIndex] = newArray(dimensions[rowIndex]);
+ Arrays.fill(mRows[rowIndex], defaultElement());
+ }
+ }
+
+ /**
+ * Construct a builder from template keyboard. This builder has the same dimensions and
+ * elements of <code>rows</rows>.
+ * @param rows the template keyboard rows. The elements of the <code>rows</code> will be
+ * shared with this builder. Therefore a element must be an immutable object.
+ */
+ AbstractKeyboardBuilder(final E[][] rows) {
+ mRows = newArrayOfArray(rows.length);
+ for (int rowIndex = 0; rowIndex < rows.length; rowIndex++) {
+ final E[] row = rows[rowIndex];
+ mRows[rowIndex] = Arrays.copyOf(row, row.length);
+ }
+ }
+
+ /**
+ * Return current constructing keyboard.
+ * @return the array of the array of the element being constructed.
+ */
+ E[][] build() {
+ return mRows;
+ }
+
+ /**
+ * Get the current contents of the specified row.
+ * @param row the row number to get the contents.
+ * @return the array of elements at row number <code>row</code>.
+ * @throws {@link RuntimeException} if <code>row</code> is illegal.
+ */
+ E[] getRowAt(final int row) {
+ final int rowIndex = row - 1;
+ if (rowIndex < 0 || rowIndex >= mRows.length) {
+ throw new RuntimeException("Illegal row number: " + row);
+ }
+ return mRows[rowIndex];
+ }
+
+ /**
+ * Set an array of elements to the specified row.
+ * @param row the row number to set <code>elements</code>.
+ * @param elements the array of elements to set at row number <code>row</code>.
+ * @throws {@link RuntimeException} if <code>row</code> is illegal.
+ */
+ void setRowAt(final int row, final E[] elements) {
+ final int rowIndex = row - 1;
+ if (rowIndex < 0 || rowIndex >= mRows.length) {
+ throw new RuntimeException("Illegal row number: " + row);
+ }
+ mRows[rowIndex] = elements;
+ }
+
+ /**
+ * Set or insert an element at specified position.
+ * @param row the row number to set or insert the <code>element</code>.
+ * @param column the column number to set or insert the <code>element</code>.
+ * @param element the element to set or insert at <code>row,column</code>.
+ * @param insert if true, the <code>element</code> is inserted at <code>row,column</code>.
+ * Otherwise the <code>element</code> replace the element at <code>row,column</code>.
+ * @throws {@link RuntimeException} if <code>row</code> or <code>column</code> is illegal.
+ */
+ void setElementAt(final int row, final int column, final E element, final boolean insert) {
+ final E[] elements = getRowAt(row);
+ final int columnIndex = column - 1;
+ if (insert) {
+ if (columnIndex < 0 || columnIndex >= elements.length + 1) {
+ throw new RuntimeException("Illegal column number: " + column);
+ }
+ final E[] newElements = Arrays.copyOf(elements, elements.length + 1);
+ // Shift the remaining elements.
+ System.arraycopy(newElements, columnIndex, newElements, columnIndex + 1,
+ elements.length - columnIndex);
+ // Insert the element at <code>row,column</code>.
+ newElements[columnIndex] = element;
+ // Replace the current row with one.
+ setRowAt(row, newElements);
+ return;
+ }
+ if (columnIndex < 0 || columnIndex >= elements.length) {
+ throw new RuntimeException("Illegal column number: " + column);
+ }
+ elements[columnIndex] = element;
+ }
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/expected/ActualKeyboardBuilder.java b/tests/src/com/android/inputmethod/keyboard/layout/expected/ActualKeyboardBuilder.java
new file mode 100644
index 000000000..b918d47f8
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/expected/ActualKeyboardBuilder.java
@@ -0,0 +1,189 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout.expected;
+
+import com.android.inputmethod.keyboard.Key;
+import com.android.inputmethod.keyboard.internal.KeyboardIconsSet;
+import com.android.inputmethod.keyboard.internal.MoreKeySpec;
+import com.android.inputmethod.latin.Constants;
+import com.android.inputmethod.latin.utils.CollectionUtils;
+import com.android.inputmethod.latin.utils.StringUtils;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+
+/**
+ * This class builds an actual keyboard for unit test.
+ */
+public final class ActualKeyboardBuilder extends AbstractKeyboardBuilder<Key> {
+ // Comparator to sort {@link Key}s from top-left to bottom-right order.
+ private static final Comparator<Key> ROW_COLUMN_COMPARATOR = new Comparator<Key>() {
+ @Override
+ public int compare(final Key lhs, final Key rhs) {
+ if (lhs.getY() < rhs.getY()) return -1;
+ if (lhs.getY() > rhs.getY()) return 1;
+ if (lhs.getX() < rhs.getX()) return -1;
+ if (lhs.getX() > rhs.getX()) return 1;
+ return 0;
+ }
+ };
+
+ /**
+ * Create the keyboard that consists of the array of rows of the actual keyboard's keys.
+ * @param keys the array of keys of the actual keyboard.
+ * @return the actual keyboard grouped with rows.
+ */
+ public static Key[][] buildKeyboard(final Key[] keys) {
+ // Sort keys from top-left to bottom-right order to prepare to create rows.
+ final ArrayList<Key> sortedKeys = CollectionUtils.newArrayList(Arrays.asList(keys));
+ Collections.sort(sortedKeys, ROW_COLUMN_COMPARATOR);
+
+ // Grouping keys into rows.
+ final ArrayList<ArrayList<Key>> rows = CollectionUtils.newArrayList();
+ ArrayList<Key> elements = CollectionUtils.newArrayList();
+ int lastY = sortedKeys.get(0).getY();
+ for (final Key key : sortedKeys) {
+ if (lastY != key.getY()) {
+ // A new row is starting.
+ lastY = key.getY();
+ rows.add(elements);
+ elements = CollectionUtils.newArrayList();
+ }
+ elements.add(key);
+ }
+ rows.add(elements); // Add the last row.
+
+ // Calculate each dimension of rows and create a builder.
+ final int[] dimensions = new int[rows.size()];
+ for (int rowIndex = 0; rowIndex < dimensions.length; rowIndex++) {
+ dimensions[rowIndex] = rows.get(rowIndex).size();
+ }
+ final ActualKeyboardBuilder builder = new ActualKeyboardBuilder(dimensions);
+
+ for (int rowIndex = 0; rowIndex < rows.size(); rowIndex++) {
+ final int row = rowIndex + 1;
+ final ArrayList<Key> rowKeys = rows.get(rowIndex);
+ builder.setRowAt(row, rowKeys.toArray(new Key[rowKeys.size()]));
+ }
+ return builder.build();
+ }
+
+ private ActualKeyboardBuilder(final int ... dimensions) {
+ super(dimensions);
+ }
+
+ @Override
+ Key defaultElement() { return null; }
+
+ @Override
+ Key[] newArray(final int size) { return new Key[size]; }
+
+ @Override
+ Key[][] newArrayOfArray(final int size) { return new Key[size][]; }
+
+ // Helper class to create concise representation from the key specification.
+ static class MoreKeySpecStringizer extends StringUtils.Stringizer<MoreKeySpec> {
+ static final MoreKeySpecStringizer STRINGIZER = new MoreKeySpecStringizer();
+
+ @Override
+ public String stringize(final MoreKeySpec spec) {
+ return toString(spec.mLabel, spec.mIconId, spec.mOutputText, spec.mCode);
+ }
+
+ static String toString(final String label, final int iconId, final String outputText,
+ final int code) {
+ final String visual = (iconId != KeyboardIconsSet.ICON_UNDEFINED)
+ ? KeyboardIconsSet.getIconName(iconId) : label;
+ final String output;
+ if (code == Constants.CODE_OUTPUT_TEXT) {
+ output = outputText;
+ } else if (code < Constants.CODE_SPACE) {
+ output = Constants.printableCode(code);
+ } else {
+ output = StringUtils.newSingleCodePointString(code);
+ }
+ if (visual.equals(output)) {
+ return visual;
+ }
+ return visual + "|" + output;
+ }
+ }
+
+ // Helper class to create concise representation from the key.
+ static class KeyStringizer extends StringUtils.Stringizer<Key> {
+ static final KeyStringizer STRINGIZER = new KeyStringizer();
+
+ @Override
+ public String stringize(final Key key) {
+ if (key == null) {
+ return "NULL";
+ }
+ if (key.isSpacer()) {
+ return "SPACER";
+ }
+ final StringBuilder sb = new StringBuilder();
+ sb.append(MoreKeySpecStringizer.toString(
+ key.getLabel(), key.getIconId(), key.getOutputText(), key.getCode()));
+ final MoreKeySpec[] moreKeys = key.getMoreKeys();
+ if (moreKeys == null) {
+ return sb.toString();
+ }
+ sb.append("^");
+ sb.append(MoreKeySpecStringizer.STRINGIZER.join(moreKeys));
+ return sb.toString();
+ }
+ }
+
+ /**
+ * Convert the key to human readable string.
+ * @param key the key to be converted to string.
+ * @return the human readable representation of <code>key</code>.
+ */
+ public static String toString(final Key key) {
+ return KeyStringizer.STRINGIZER.stringize(key);
+ }
+
+ /**
+ * Convert the keyboard row to human readable string.
+ * @param keys the keyboard row to be converted to string.
+ * @return the human readable representation of <code>keys</code>.
+ */
+ public static String toString(final Key[] keys) {
+ return KeyStringizer.STRINGIZER.join(keys);
+ }
+
+ // Helper class to create concise representation from the array of the key.
+ static class KeyArrayStringizer extends StringUtils.Stringizer<Key[]> {
+ static final KeyArrayStringizer STRINGIZER = new KeyArrayStringizer();
+
+ @Override
+ public String stringize(final Key[] keyArray) {
+ return KeyStringizer.STRINGIZER.join(keyArray);
+ }
+ }
+
+ /**
+ * Convert the keyboard to human readable string.
+ * @param rows the keyboard to be converted to string.
+ * @return the human readable representation of <code>rows</code>.
+ */
+ public static String toString(final Key[][] rows) {
+ return KeyArrayStringizer.STRINGIZER.join(rows, "\n" /* delimiter */);
+ }
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/expected/ExpectedKey.java b/tests/src/com/android/inputmethod/keyboard/layout/expected/ExpectedKey.java
new file mode 100644
index 000000000..e22d75cd7
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/expected/ExpectedKey.java
@@ -0,0 +1,186 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout.expected;
+
+import com.android.inputmethod.keyboard.Key;
+import com.android.inputmethod.keyboard.internal.MoreKeySpec;
+
+import java.util.Arrays;
+import java.util.Locale;
+
+/**
+ * This class represents an expected key.
+ */
+public class ExpectedKey {
+ static ExpectedKey EMPTY_KEY = newInstance("");
+
+ // A key that has a string label and may have "more keys".
+ static ExpectedKey newInstance(final String label, final ExpectedKey ... moreKeys) {
+ return newInstance(label, label, moreKeys);
+ }
+
+ // A key that has a string label and a different output text and may have "more keys".
+ static ExpectedKey newInstance(final String label, final String outputText,
+ final ExpectedKey ... moreKeys) {
+ return newInstance(ExpectedKeyVisual.newInstance(label),
+ ExpectedKeyOutput.newInstance(outputText), moreKeys);
+ }
+
+ // A key that has a string label and a code point output and may have "more keys".
+ static ExpectedKey newInstance(final String label, final int code,
+ final ExpectedKey ... moreKeys) {
+ return newInstance(ExpectedKeyVisual.newInstance(label),
+ ExpectedKeyOutput.newInstance(code), moreKeys);
+ }
+
+ // A key that has an icon and a code point output and may have "more keys".
+ static ExpectedKey newInstance(final int iconId, final int code,
+ final ExpectedKey ... moreKeys) {
+ return newInstance(ExpectedKeyVisual.newInstance(iconId),
+ ExpectedKeyOutput.newInstance(code), moreKeys);
+ }
+
+ static ExpectedKey newInstance(final ExpectedKeyVisual visual, final ExpectedKeyOutput output,
+ final ExpectedKey ... moreKeys) {
+ if (moreKeys.length == 0) {
+ return new ExpectedKey(visual, output);
+ }
+ return new ExpectedKeyWithMoreKeys(visual, output, moreKeys);
+ }
+
+ private static final ExpectedKey[] EMPTY_KEYS = new ExpectedKey[0];
+
+ // The expected visual outlook of this key.
+ private final ExpectedKeyVisual mVisual;
+ // The expected output of this key.
+ private final ExpectedKeyOutput mOutput;
+
+ public final ExpectedKeyVisual getVisual() {
+ return mVisual;
+ }
+
+ public final ExpectedKeyOutput getOutput() {
+ return mOutput;
+ }
+
+ public ExpectedKey[] getMoreKeys() {
+ // This key has no "more keys".
+ return EMPTY_KEYS;
+ }
+
+ protected ExpectedKey(final ExpectedKeyVisual visual, final ExpectedKeyOutput output) {
+ mVisual = visual;
+ mOutput = output;
+ }
+
+ public ExpectedKey toUpperCase(Locale locale) {
+ return newInstance(mVisual.toUpperCase(locale), mOutput.toUpperCase(locale));
+ }
+
+ public boolean equalsTo(final Key key) {
+ // This key has no "more keys".
+ return mVisual.equalsTo(key) && mOutput.equalsTo(key) && key.getMoreKeys() == null;
+ }
+
+ public boolean equalsTo(final MoreKeySpec moreKeySpec) {
+ return mVisual.equalsTo(moreKeySpec) && mOutput.equalsTo(moreKeySpec);
+ }
+
+ @Override
+ public boolean equals(final Object object) {
+ if (object instanceof ExpectedKey) {
+ final ExpectedKey key = (ExpectedKey)object;
+ return mVisual.equalsTo(key.mVisual) && mOutput.equalsTo(key.mOutput)
+ && Arrays.equals(getMoreKeys(), key.getMoreKeys());
+ }
+ return false;
+ }
+
+ private static int hashCode(final Object ... objects) {
+ return Arrays.hashCode(objects);
+ }
+
+ @Override
+ public int hashCode() {
+ return hashCode(mVisual, mOutput, getMoreKeys());
+ }
+
+ @Override
+ public String toString() {
+ if (mVisual.equalsTo(mOutput)) {
+ return mVisual.toString();
+ }
+ return mVisual + "|" + mOutput;
+ }
+
+ /**
+ * This class represents an expected key that has "more keys".
+ */
+ private static final class ExpectedKeyWithMoreKeys extends ExpectedKey {
+ private final ExpectedKey[] mMoreKeys;
+
+ ExpectedKeyWithMoreKeys(final ExpectedKeyVisual visual,
+ final ExpectedKeyOutput output, final ExpectedKey ... moreKeys) {
+ super(visual, output);
+ mMoreKeys = moreKeys;
+ }
+
+ @Override
+ public ExpectedKey toUpperCase(final Locale locale) {
+ final ExpectedKey[] upperCaseMoreKeys = new ExpectedKey[mMoreKeys.length];
+ for (int i = 0; i < mMoreKeys.length; i++) {
+ upperCaseMoreKeys[i] = mMoreKeys[i].toUpperCase(locale);
+ }
+ return newInstance(getVisual().toUpperCase(locale), getOutput().toUpperCase(locale),
+ upperCaseMoreKeys);
+ }
+
+ @Override
+ public ExpectedKey[] getMoreKeys() {
+ return mMoreKeys;
+ }
+
+ @Override
+ public boolean equalsTo(final Key key) {
+ if (getVisual().equalsTo(key) && getOutput().equalsTo(key)) {
+ final MoreKeySpec[] moreKeys = key.getMoreKeys();
+ // This key should have at least one "more key".
+ if (moreKeys == null || moreKeys.length != mMoreKeys.length) {
+ return false;
+ }
+ for (int index = 0; index < moreKeys.length; index++) {
+ if (!mMoreKeys[index].equalsTo(moreKeys[index])) {
+ return false;
+ }
+ }
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public boolean equalsTo(final MoreKeySpec moreKeySpec) {
+ // MoreKeySpec has no "more keys".
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ return super.toString() + "^" + Arrays.toString(mMoreKeys);
+ }
+ }
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/expected/ExpectedKeyOutput.java b/tests/src/com/android/inputmethod/keyboard/layout/expected/ExpectedKeyOutput.java
new file mode 100644
index 000000000..1be51e60b
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/expected/ExpectedKeyOutput.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout.expected;
+
+import com.android.inputmethod.keyboard.Key;
+import com.android.inputmethod.keyboard.internal.MoreKeySpec;
+import com.android.inputmethod.latin.Constants;
+import com.android.inputmethod.latin.utils.StringUtils;
+
+import java.util.Locale;
+
+/**
+ * This class represents an expected output of a key.
+ *
+ * There are two types of expected output, an integer code point and a string output text.
+ */
+abstract class ExpectedKeyOutput {
+ static ExpectedKeyOutput newInstance(final int code) {
+ return new Code(code);
+ }
+
+ static ExpectedKeyOutput newInstance(final String outputText) {
+ // If the <code>outputText</code> is one code point string, use {@link CodePoint} object.
+ if (StringUtils.codePointCount(outputText) == 1) {
+ return new Code(outputText.codePointAt(0));
+ }
+ return new Text(outputText);
+ }
+
+ abstract ExpectedKeyOutput toUpperCase(final Locale locale);
+ abstract boolean equalsTo(final String text);
+ abstract boolean equalsTo(final Key key);
+ abstract boolean equalsTo(final MoreKeySpec moreKeySpec);
+ abstract boolean equalsTo(final ExpectedKeyOutput output);
+
+ /**
+ * This class represents an integer code point.
+ */
+ private static class Code extends ExpectedKeyOutput {
+ // UNICODE code point or a special negative value defined in {@link Constants}.
+ private final int mCode;
+
+ Code(final int code) { mCode = code; }
+
+ @Override
+ ExpectedKeyOutput toUpperCase(final Locale locale) {
+ if (Constants.isLetterCode(mCode)) {
+ final String codeString = StringUtils.newSingleCodePointString(mCode);
+ // A letter may have an upper case counterpart that consists of multiple code
+ // points, for instance the upper case of "ß" is "SS".
+ return newInstance(codeString.toUpperCase(locale));
+ }
+ // A special negative value has no upper case.
+ return this;
+ }
+
+ @Override
+ boolean equalsTo(final String text) {
+ return StringUtils.codePointCount(text) == 1 && text.codePointAt(0) == mCode;
+ }
+
+ @Override
+ boolean equalsTo(final Key key) {
+ return mCode == key.getCode();
+ }
+
+ @Override
+ boolean equalsTo(final MoreKeySpec moreKeySpec) {
+ return mCode == moreKeySpec.mCode;
+ }
+
+ @Override
+ boolean equalsTo(final ExpectedKeyOutput output) {
+ return (output instanceof Code) && mCode == ((Code)output).mCode;
+ }
+
+ @Override
+ public String toString() {
+ return Constants.isLetterCode(mCode) ? StringUtils.newSingleCodePointString(mCode)
+ : Constants.printableCode(mCode);
+ }
+ }
+
+ /**
+ * This class represents a string output text.
+ */
+ private static class Text extends ExpectedKeyOutput {
+ private final String mText;
+
+ Text(final String text) { mText = text; }
+
+ @Override
+ ExpectedKeyOutput toUpperCase(final Locale locale) {
+ return newInstance(mText.toUpperCase(locale));
+ }
+
+ @Override
+ boolean equalsTo(final String text) {
+ return text.equals(text);
+ }
+
+ @Override
+ boolean equalsTo(final Key key) {
+ return key.getCode() == Constants.CODE_OUTPUT_TEXT
+ && mText.equals(key.getOutputText());
+ }
+
+ @Override
+ boolean equalsTo(final MoreKeySpec moreKeySpec) {
+ return moreKeySpec.mCode == Constants.CODE_OUTPUT_TEXT
+ && mText.equals(moreKeySpec.mOutputText);
+ }
+
+ @Override
+ boolean equalsTo(final ExpectedKeyOutput output) {
+ return (output instanceof Text) && mText == ((Text)output).mText;
+ }
+
+ @Override
+ public String toString() {
+ return mText;
+ }
+ }
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/expected/ExpectedKeyVisual.java b/tests/src/com/android/inputmethod/keyboard/layout/expected/ExpectedKeyVisual.java
new file mode 100644
index 000000000..0a0da32b6
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/expected/ExpectedKeyVisual.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout.expected;
+
+import com.android.inputmethod.keyboard.Key;
+import com.android.inputmethod.keyboard.internal.KeyboardIconsSet;
+import com.android.inputmethod.keyboard.internal.MoreKeySpec;
+
+import java.util.Locale;
+
+/**
+ * This class represents an expected visual outlook of a key.
+ *
+ * There are two types of expected visual, an integer icon id and a string label.
+ */
+abstract class ExpectedKeyVisual {
+ static ExpectedKeyVisual newInstance(final String label) {
+ return new Label(label);
+ }
+
+ static ExpectedKeyVisual newInstance(final int iconId) {
+ return new Icon(iconId);
+ }
+
+ abstract ExpectedKeyVisual toUpperCase(final Locale locale);
+ abstract boolean equalsTo(final String text);
+ abstract boolean equalsTo(final Key key);
+ abstract boolean equalsTo(final MoreKeySpec moreKeySpec);
+ abstract boolean equalsTo(final ExpectedKeyOutput output);
+ abstract boolean equalsTo(final ExpectedKeyVisual visual);
+
+ /**
+ * This class represents an integer icon id.
+ */
+ private static class Icon extends ExpectedKeyVisual {
+ private final int mIconId;
+
+ Icon(final int iconId) {
+ mIconId = iconId;
+ }
+
+ @Override
+ ExpectedKeyVisual toUpperCase(final Locale locale) {
+ return this;
+ }
+
+ @Override
+ boolean equalsTo(final String text) {
+ return false;
+ }
+
+ @Override
+ boolean equalsTo(final Key key) {
+ return mIconId == key.getIconId();
+ }
+
+ @Override
+ boolean equalsTo(final MoreKeySpec moreKeySpec) {
+ return mIconId == moreKeySpec.mIconId;
+ }
+
+ @Override
+ boolean equalsTo(final ExpectedKeyOutput output) {
+ return false;
+ }
+
+ @Override
+ boolean equalsTo(final ExpectedKeyVisual visual) {
+ return (visual instanceof Icon) && mIconId == ((Icon)visual).mIconId;
+ }
+
+ @Override
+ public String toString() {
+ return KeyboardIconsSet.getIconName(mIconId);
+ }
+ }
+
+ /**
+ * This class represents a string label.
+ */
+ private static class Label extends ExpectedKeyVisual {
+ private final String mLabel;
+
+ Label(final String label) { mLabel = label; }
+
+ @Override
+ ExpectedKeyVisual toUpperCase(final Locale locale) {
+ return new Label(mLabel.toUpperCase(locale));
+ }
+
+ @Override
+ boolean equalsTo(final String text) {
+ return mLabel.equals(text);
+ }
+
+ @Override
+ boolean equalsTo(final Key key) {
+ return mLabel.equals(key.getLabel());
+ }
+
+ @Override
+ boolean equalsTo(final MoreKeySpec moreKeySpec) {
+ return mLabel.equals(moreKeySpec.mLabel);
+ }
+
+ @Override
+ boolean equalsTo(final ExpectedKeyOutput output) {
+ return output.equalsTo(mLabel);
+ }
+
+ @Override
+ boolean equalsTo(final ExpectedKeyVisual visual) {
+ return (visual instanceof Label) && mLabel.equals(((Label)visual).mLabel);
+ }
+
+ @Override
+ public String toString() {
+ return mLabel;
+ }
+ }
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/expected/ExpectedKeyboardBuilder.java b/tests/src/com/android/inputmethod/keyboard/layout/expected/ExpectedKeyboardBuilder.java
new file mode 100644
index 000000000..61288f048
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/expected/ExpectedKeyboardBuilder.java
@@ -0,0 +1,272 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout.expected;
+
+import java.util.Arrays;
+import java.util.Locale;
+
+/**
+ * This class builds an expected keyboard for unit test.
+ */
+public final class ExpectedKeyboardBuilder extends AbstractKeyboardBuilder<ExpectedKey> {
+ public ExpectedKeyboardBuilder(final int ... dimensions) {
+ super(dimensions);
+ }
+
+ public ExpectedKeyboardBuilder(final ExpectedKey[][] rows) {
+ super(rows);
+ }
+
+ @Override
+ protected ExpectedKey defaultElement() {
+ return ExpectedKey.EMPTY_KEY;
+ }
+
+ @Override
+ ExpectedKey[] newArray(final int size) {
+ return new ExpectedKey[size];
+ }
+
+ @Override
+ ExpectedKey[][] newArrayOfArray(final int size) {
+ return new ExpectedKey[size][];
+ }
+
+ @Override
+ public ExpectedKey[][] build() {
+ return super.build();
+ }
+
+ // A replacement job to be performed.
+ interface ReplaceJob {
+ // Returns a {@link ExpectedKey} object to replace.
+ ExpectedKey replace(final ExpectedKey oldKey);
+ // Return true if replacing should be stopped at first occurrence.
+ boolean stopAtFirstOccurrence();
+ }
+
+ // Replace key(s) that has the specified visual.
+ private void replaceKeyOf(final ExpectedKeyVisual visual, final ReplaceJob job) {
+ int replacedCount = 0;
+ final ExpectedKey[][] rows = build();
+ for (int rowIndex = 0; rowIndex < rows.length; rowIndex++) {
+ final ExpectedKey[] keys = rows[rowIndex];
+ for (int columnIndex = 0; columnIndex < keys.length; columnIndex++) {
+ if (keys[columnIndex].getVisual().equalsTo(visual)) {
+ keys[columnIndex] = job.replace(keys[columnIndex]);
+ replacedCount++;
+ if (job.stopAtFirstOccurrence()) {
+ return;
+ }
+ }
+ }
+ }
+ if (replacedCount == 0) {
+ throw new RuntimeException(
+ "Can't find key that has visual: " + visual + " in\n" + toString(rows));
+ }
+ }
+
+ /**
+ * Set the row with specified keys that have specified labels.
+ * @param row the row number to set keys.
+ * @param labels the label texts of the keys.
+ * @return this builder.
+ */
+ public ExpectedKeyboardBuilder setLabelsOfRow(final int row, final String ... labels) {
+ final ExpectedKey[] keys = new ExpectedKey[labels.length];
+ for (int columnIndex = 0; columnIndex < labels.length; columnIndex++) {
+ keys[columnIndex] = ExpectedKey.newInstance(labels[columnIndex]);
+ }
+ setRowAt(row, keys);
+ return this;
+ }
+
+ /**
+ * Set the "more keys" of the key that has the specified label.
+ * @param label the label of the key to set the "more keys".
+ * @param moreKeys the array of labels of the "more keys" to be set.
+ * @return this builder.
+ */
+ public ExpectedKeyboardBuilder setMoreKeysOf(final String label, final String ... moreKeys) {
+ final ExpectedKey[] expectedMoreKeys = new ExpectedKey[moreKeys.length];
+ for (int index = 0; index < moreKeys.length; index++) {
+ expectedMoreKeys[index] = ExpectedKey.newInstance(moreKeys[index]);
+ }
+ setMoreKeysOf(label, expectedMoreKeys);
+ return this;
+ }
+
+ /**
+ * Set the "more keys" of the key that has the specified label.
+ * @param label the label of the key to set the "more keys".
+ * @param moreKeys the array of "more key" to be set.
+ * @return this builder.
+ */
+ public ExpectedKeyboardBuilder setMoreKeysOf(final String label,
+ final ExpectedKey ... moreKeys) {
+ setMoreKeysOf(ExpectedKeyVisual.newInstance(label), moreKeys);
+ return this;
+ }
+
+ /**
+ * Set the "more keys" of the key that has the specified icon.
+ * @param iconId the icon id of the key to set the "more keys".
+ * @param moreKeys the array of "more key" to be set.
+ * @return this builder.
+ */
+ public ExpectedKeyboardBuilder setMoreKeysOf(final int iconId, final ExpectedKey ... moreKeys) {
+ setMoreKeysOf(ExpectedKeyVisual.newInstance(iconId), moreKeys);
+ return this;
+ }
+
+ private void setMoreKeysOf(final ExpectedKeyVisual visual, final ExpectedKey[] moreKeys) {
+ replaceKeyOf(visual, new ReplaceJob() {
+ @Override
+ public ExpectedKey replace(final ExpectedKey oldKey) {
+ return ExpectedKey.newInstance(oldKey.getVisual(), oldKey.getOutput(), moreKeys);
+ }
+ @Override
+ public boolean stopAtFirstOccurrence() {
+ return true;
+ }
+ });
+ }
+
+ /**
+ * Insert the keys at specified position.
+ * @param row the row number to insert the <code>keys</code>.
+ * @param column the column number to insert the <code>keys</code>.
+ * @param keys the array of keys to insert at <code>row,column</code>.
+ * @return this builder.
+ * @throws {@link RuntimeException} if <code>row</code> or <code>column</code> is illegal.
+ */
+ public ExpectedKeyboardBuilder insertKeysAtRow(final int row, final int column,
+ final ExpectedKey ... keys) {
+ for (int index = 0; index < keys.length; index++) {
+ setElementAt(row, column + index, keys[index], true /* insert */);
+ }
+ return this;
+ }
+
+ /**
+ * Add the keys on the left most of the row.
+ * @param row the row number to add the <code>keys</code>.
+ * @param keys the array of keys to add on the left most of the row.
+ * @return this builder.
+ * @throws {@link RuntimeException} if <code>row</code> is illegal.
+ */
+ public ExpectedKeyboardBuilder addKeysOnTheLeftOfRow(final int row,
+ final ExpectedKey ... keys) {
+ // Keys should be inserted from the last to preserve the order.
+ for (int index = keys.length - 1; index >= 0; index--) {
+ setElementAt(row, 1, keys[index], true /* insert */);
+ }
+ return this;
+ }
+
+ /**
+ * Add the keys on the right most of the row.
+ * @param row the row number to add the <code>keys</code>.
+ * @param keys the array of keys to add on the right most of the row.
+ * @return this builder.
+ * @throws {@link RuntimeException} if <code>row</code> is illegal.
+ */
+ public ExpectedKeyboardBuilder addKeysOnTheRightOfRow(final int row,
+ final ExpectedKey ... keys) {
+ final int rightEnd = getRowAt(row).length + 1;
+ insertKeysAtRow(row, rightEnd, keys);
+ return this;
+ }
+
+ /**
+ * Replace the most top-left key that has the specified label with the new key.
+ * @param label the label of the key to set <code>newKey</code>.
+ * @param newKey the key to be set.
+ * @return this builder.
+ */
+ public ExpectedKeyboardBuilder replaceKeyOfLabel(final String label, final ExpectedKey newKey) {
+ final ExpectedKeyVisual visual = ExpectedKeyVisual.newInstance(label);
+ replaceKeyOf(visual, new ReplaceJob() {
+ @Override
+ public ExpectedKey replace(final ExpectedKey oldKey) {
+ return newKey;
+ }
+ @Override
+ public boolean stopAtFirstOccurrence() {
+ return true;
+ }
+ });
+ return this;
+ }
+
+ /**
+ * Replace the all specified keys with the new key.
+ * @param key the key to be replaced by <code>newKey</code>.
+ * @param newKey the key to be set.
+ * @return this builder.
+ */
+ public ExpectedKeyboardBuilder replaceKeyOfAll(final ExpectedKey key,
+ final ExpectedKey newKey) {
+ replaceKeyOf(key.getVisual(), new ReplaceJob() {
+ @Override
+ public ExpectedKey replace(final ExpectedKey oldKey) {
+ return newKey;
+ }
+ @Override
+ public boolean stopAtFirstOccurrence() {
+ return false;
+ }
+ });
+ return this;
+ }
+
+ /**
+ * Returns new keyboard instance that has upper case keys of the specified keyboard.
+ * @param rows the lower case keyboard.
+ * @param locale the locale used to convert cases.
+ * @return the upper case keyboard.
+ */
+ public static ExpectedKey[][] toUpperCase(final ExpectedKey[][] rows, final Locale locale) {
+ final ExpectedKey[][] upperCaseRows = new ExpectedKey[rows.length][];
+ for (int rowIndex = 0; rowIndex < rows.length; rowIndex++) {
+ final ExpectedKey[] lowerCaseKeys = rows[rowIndex];
+ final ExpectedKey[] upperCaseKeys = new ExpectedKey[lowerCaseKeys.length];
+ for (int columnIndex = 0; columnIndex < lowerCaseKeys.length; columnIndex++) {
+ upperCaseKeys[columnIndex] = lowerCaseKeys[columnIndex].toUpperCase(locale);
+ }
+ upperCaseRows[rowIndex] = upperCaseKeys;
+ }
+ return upperCaseRows;
+ }
+
+ /**
+ * Convert the keyboard to human readable string.
+ * @param rows the keyboard to be converted to string.
+ * @return the human readable representation of <code>rows</code>.
+ */
+ public static String toString(final ExpectedKey[][] rows) {
+ final StringBuilder sb = new StringBuilder();
+ for (int rowIndex = 0; rowIndex < rows.length; rowIndex++) {
+ if (rowIndex > 0) {
+ sb.append("\n");
+ }
+ sb.append(Arrays.toString(rows[rowIndex]));
+ }
+ return sb.toString();
+ }
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/expected/LayoutBase.java b/tests/src/com/android/inputmethod/keyboard/layout/expected/LayoutBase.java
new file mode 100644
index 000000000..1aeb8c0cd
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/expected/LayoutBase.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout.expected;
+
+import com.android.inputmethod.keyboard.internal.KeyboardIconsSet;
+import com.android.inputmethod.latin.Constants;
+
+/**
+ * Base class to create an expected keyboard for unit test.
+ */
+public class LayoutBase {
+ // Those helper methods have a lower case name to be readable when defining expected keyboard
+ // layouts.
+
+ // Helper method to create {@link ExpectedKey} object that has the label.
+ public static ExpectedKey key(final String label, final ExpectedKey ... moreKeys) {
+ return ExpectedKey.newInstance(label, moreKeys);
+ }
+
+ // Helper method to create {@link ExpectedKey} object that has the label and the output text.
+ public static ExpectedKey key(final String label, final String outputText,
+ final ExpectedKey ... moreKeys) {
+ return ExpectedKey.newInstance(label, outputText, moreKeys);
+ }
+
+ // Helper method to create {@link ExpectedKey} object that has the label and the output code.
+ public static ExpectedKey key(final String label, final int code,
+ final ExpectedKey ... moreKeys) {
+ return ExpectedKey.newInstance(label, code, moreKeys);
+ }
+
+ // Helper method to create {@link ExpectedKey} object that has the icon and the output code.
+ public static ExpectedKey key(final int iconId, final int code,
+ final ExpectedKey ... moreKeys) {
+ return ExpectedKey.newInstance(iconId, code, moreKeys);
+ }
+
+ // Helper method to create {@link ExpectedKey} object that has new "more keys".
+ public static ExpectedKey key(final ExpectedKey key, final ExpectedKey ... moreKeys) {
+ return ExpectedKey.newInstance(key.getVisual(), key.getOutput(), moreKeys);
+ }
+
+ // Helper method to create {@link ExpectedKey} object for "more key" that has the label.
+ public static ExpectedKey moreKey(final String label) {
+ return ExpectedKey.newInstance(label);
+ }
+
+ // Helper method to create {@link ExpectedKey} object for "more key" that has the label and the
+ // output text.
+ public static ExpectedKey moreKey(final String label, final String outputText) {
+ return ExpectedKey.newInstance(label, outputText);
+ }
+
+ // Helper method to create {@link ExpectedKey} object for "more key" that has the label and the
+ // output code.
+ public static ExpectedKey moreKey(final String label, final int code) {
+ return ExpectedKey.newInstance(label, code);
+ }
+
+ // Icon ids.
+ private static final int ICON_SHIFT = KeyboardIconsSet.getIconId("shift_key");
+ private static final int ICON_DELETE = KeyboardIconsSet.getIconId("delete_key");
+ private static final int ICON_SETTINGS = KeyboardIconsSet.getIconId("settings_key");
+ private static final int ICON_ENTER = KeyboardIconsSet.getIconId("enter_key");
+ private static final int ICON_EMOJI = KeyboardIconsSet.getIconId("emoji_key");
+
+ // Functional keys.
+ public static final ExpectedKey CAPSLOCK_MORE_KEY = key(" ", Constants.CODE_CAPSLOCK);
+ public static final ExpectedKey SHIFT_KEY = key(ICON_SHIFT, Constants.CODE_SHIFT);
+ public static final ExpectedKey DELETE_KEY = key(ICON_DELETE, Constants.CODE_DELETE);
+ public static final ExpectedKey SYMBOLS_KEY = key("?123", Constants.CODE_SWITCH_ALPHA_SYMBOL);
+ public static final ExpectedKey SETTINGS_KEY = key(ICON_SETTINGS, Constants.CODE_SETTINGS);
+ public static final ExpectedKey ENTER_KEY = key(ICON_ENTER, Constants.CODE_ENTER);
+ public static final ExpectedKey EMOJI_KEY = key(ICON_EMOJI, Constants.CODE_EMOJI);
+
+ // Punctuation more keys for phone form factor.
+ public static final String[] PHONE_PUNCTUATION_MORE_KEYS = {
+ ";", "/", "(", ")", "#", "!", ",", "?",
+ "&", "%", "+", "\"", "-", ":", "'", "@"
+ };
+
+ // Punctuation more keys for tablet form factor.
+ public static final String[] TABLET_PUNCTUATION_MORE_KEYS = {
+ ";", "/", "(", ")", "#", "'", ",",
+ "&", "%", "+", "\"", "-", ":", "@"
+ };
+
+ private static ExpectedKeyboardBuilder toPhoneAlphabet(final ExpectedKeyboardBuilder builder) {
+ return builder
+ .addKeysOnTheLeftOfRow(3, key(SHIFT_KEY, CAPSLOCK_MORE_KEY))
+ .addKeysOnTheRightOfRow(3, DELETE_KEY)
+ .setLabelsOfRow(4, ",", " ", ".")
+ .setMoreKeysOf(",", SETTINGS_KEY)
+ .setMoreKeysOf(".", PHONE_PUNCTUATION_MORE_KEYS)
+ .addKeysOnTheLeftOfRow(4, SYMBOLS_KEY)
+ .addKeysOnTheRightOfRow(4, key(ENTER_KEY, EMOJI_KEY));
+ }
+
+ // Helper method to create alphabet layout for tablet by adding special function keys except
+ // shift key.
+ public static ExpectedKeyboardBuilder toTabletAlphabetWithoutShiftKeys(
+ final ExpectedKeyboardBuilder builder) {
+ return builder
+ // U+00BF: "¿" INVERTED QUESTION MARK
+ // U+00A1: "¡" INVERTED EXCLAMATION MARK
+ .addKeysOnTheRightOfRow(3,
+ key("!", moreKey("\u00A1")), key("?", moreKey("\u00BF")))
+ .addKeysOnTheRightOfRow(1, DELETE_KEY)
+ .addKeysOnTheRightOfRow(2, ENTER_KEY)
+ .setLabelsOfRow(4, "/", " ", ",", ".")
+ .setMoreKeysOf(".", TABLET_PUNCTUATION_MORE_KEYS)
+ .addKeysOnTheLeftOfRow(4, SYMBOLS_KEY, SETTINGS_KEY)
+ .addKeysOnTheRightOfRow(4, EMOJI_KEY);
+ }
+
+ // Helper method to create alphabet layout by adding special function keys.
+ public static ExpectedKey[][] toCommonAlphabet(final ExpectedKey[][] common,
+ final boolean isPhone) {
+ final ExpectedKeyboardBuilder builder = new ExpectedKeyboardBuilder(common);
+ if (isPhone) {
+ toPhoneAlphabet(builder);
+ } else {
+ toTabletAlphabetWithoutShiftKeys(builder);
+ builder.addKeysOnTheLeftOfRow(3, key(SHIFT_KEY, CAPSLOCK_MORE_KEY))
+ .addKeysOnTheRightOfRow(3, key(SHIFT_KEY, CAPSLOCK_MORE_KEY));
+ }
+ return builder.build();
+ }
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/LayoutTestsBase.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/LayoutTestsBase.java
new file mode 100644
index 000000000..427e7de49
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/LayoutTestsBase.java
@@ -0,0 +1,197 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout.tests;
+
+import android.util.Log;
+import android.view.inputmethod.InputMethodSubtype;
+
+import com.android.inputmethod.keyboard.Key;
+import com.android.inputmethod.keyboard.Keyboard;
+import com.android.inputmethod.keyboard.KeyboardId;
+import com.android.inputmethod.keyboard.KeyboardLayoutSet;
+import com.android.inputmethod.keyboard.KeyboardLayoutSetTestsBase;
+import com.android.inputmethod.keyboard.layout.AlphabetShifted;
+import com.android.inputmethod.keyboard.layout.Symbols;
+import com.android.inputmethod.keyboard.layout.SymbolsShifted;
+import com.android.inputmethod.keyboard.layout.expected.ActualKeyboardBuilder;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
+import com.android.inputmethod.keyboard.layout.expected.LayoutBase;
+import com.android.inputmethod.latin.utils.SubtypeLocaleUtils;
+
+import java.util.Arrays;
+import java.util.Locale;
+
+/**
+ * Base class for keyboard layout unit test.
+ */
+abstract class LayoutTestsBase extends KeyboardLayoutSetTestsBase {
+ private InputMethodSubtype mSubtype;
+ private String mLogTag;
+ private KeyboardLayoutSet mKeyboardLayoutSet;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+
+ mSubtype = getSubtype(getTestLocale(), getTestKeyboardLayout());
+ mLogTag = SubtypeLocaleUtils.getSubtypeNameForLogging(mSubtype) + "/"
+ + (isPhone() ? "phone" : "tablet");
+ mKeyboardLayoutSet = createKeyboardLayoutSet(mSubtype, null /* editorInfo */);
+ }
+
+ // Those helper methods have a lower case name to be readable when defining expected keyboard
+ // layouts.
+
+ // Helper method to create {@link ExpectedKey} object that has the label.
+ static ExpectedKey key(final String label, final ExpectedKey ... moreKeys) {
+ return LayoutBase.key(label, moreKeys);
+ }
+
+ // Helper method to create {@link ExpectedKey} object that has the label and the output text.
+ static ExpectedKey key(final String label, final String outputText,
+ final ExpectedKey ... moreKeys) {
+ return LayoutBase.key(label, outputText, moreKeys);
+ }
+
+ // Helper method to create {@link ExpectedKey} object for "more key" that has the label.
+ static ExpectedKey moreKey(final String label) {
+ return LayoutBase.moreKey(label);
+ }
+
+ // Helper method to create {@link ExpectedKey} object for "more key" that has the label and the
+ // output text.
+ static ExpectedKey moreKey(final String label, final String outputText) {
+ return LayoutBase.moreKey(label, outputText);
+ }
+
+ // Locale for testing subtype.
+ abstract Locale getTestLocale();
+
+ // Keyboard layout name for testing subtype.
+ abstract String getTestKeyboardLayout();
+
+ // Alphabet keyboard for testing subtype.
+ abstract ExpectedKey[][] getAlphabet(final boolean isPhone);
+
+ // Alphabet automatic shifted keyboard for testing subtype.
+ ExpectedKey[][] getAlphabetAutomaticShifted(final boolean isPhone) {
+ return AlphabetShifted.getAlphabet(getAlphabet(isPhone), getTestLocale());
+ }
+
+ // Alphabet manual shifted keyboard for testing subtype.
+ ExpectedKey[][] getAlphabetManualShifted(final boolean isPhone) {
+ return AlphabetShifted.getAlphabet(getAlphabet(isPhone), getTestLocale());
+ }
+
+ // Alphabet shift locked keyboard for testing subtype.
+ ExpectedKey[][] getAlphabetShiftLocked(final boolean isPhone) {
+ return AlphabetShifted.getAlphabet(getAlphabet(isPhone), getTestLocale());
+ }
+
+ // Alphabet shift lock shifted keyboard for testing subtype.
+ ExpectedKey[][] getAlphabetShiftLockShifted(final boolean isPhone) {
+ return AlphabetShifted.getAlphabet(getAlphabet(isPhone), getTestLocale());
+ }
+
+ // Symbols keyboard for testing subtype.
+ ExpectedKey[][] getSymbols(final boolean isPhone) {
+ return Symbols.getSymbols(isPhone);
+ }
+
+ // Symbols shifted keyboard for testing subtype.
+ ExpectedKey[][] getSymbolsShifted(final boolean isPhone) {
+ return SymbolsShifted.getSymbolsShifted(isPhone);
+ }
+
+ // TODO: Add phone, phone symbols, number, number password layout tests.
+
+ public final void testAlphabet() {
+ final int elementId = KeyboardId.ELEMENT_ALPHABET;
+ doKeyboardTests(elementId, getAlphabet(isPhone()));
+ }
+
+ public final void testAlphabetAutomaticShifted() {
+ final int elementId = KeyboardId.ELEMENT_ALPHABET_AUTOMATIC_SHIFTED;
+ doKeyboardTests(elementId, getAlphabetAutomaticShifted(isPhone()));
+ }
+
+ public final void testAlphabetManualShifted() {
+ final int elementId = KeyboardId.ELEMENT_ALPHABET_MANUAL_SHIFTED;
+ doKeyboardTests(elementId, getAlphabetManualShifted(isPhone()));
+ }
+
+ public final void testAlphabetShiftLocked() {
+ final int elementId = KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCKED;
+ doKeyboardTests(elementId, getAlphabetShiftLocked(isPhone()));
+ }
+
+ public final void testAlphabetShiftLockShifted() {
+ final int elementId = KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCK_SHIFTED;
+ doKeyboardTests(elementId, getAlphabetShiftLockShifted(isPhone()));
+ }
+
+ public final void testSymbols() {
+ final int elementId = KeyboardId.ELEMENT_SYMBOLS;
+ doKeyboardTests(elementId, getSymbols(isPhone()));
+ }
+
+ public final void testSymbolsShifted() {
+ final int elementId = KeyboardId.ELEMENT_SYMBOLS_SHIFTED;
+ doKeyboardTests(elementId, getSymbolsShifted(isPhone()));
+ }
+
+ // Comparing expected keyboard and actual keyboard.
+ private void doKeyboardTests(final int elementId, final ExpectedKey[][] expectedKeyboard) {
+ // Skip test if no keyboard is defined.
+ if (expectedKeyboard == null) {
+ return;
+ }
+ final String tag = mLogTag + "/" + KeyboardId.elementIdToName(elementId);
+ // Create actual keyboard object.
+ final Keyboard keyboard = mKeyboardLayoutSet.getKeyboard(elementId);
+ // Create actual keyboard to be compared with the expected keyboard.
+ final Key[][] actualKeyboard = ActualKeyboardBuilder.buildKeyboard(keyboard.getKeys());
+
+ // Dump human readable definition of expected/actual keyboards.
+ Log.d(tag, "expected=\n" + ExpectedKeyboardBuilder.toString(expectedKeyboard));
+ Log.d(tag, "actual =\n" + ActualKeyboardBuilder.toString(actualKeyboard));
+ // Test both keyboards have the same number of rows.
+ assertEquals(tag + " labels"
+ + "\nexpected=" + Arrays.deepToString(expectedKeyboard)
+ + "\nactual =" + ActualKeyboardBuilder.toString(actualKeyboard),
+ expectedKeyboard.length, actualKeyboard.length);
+ for (int r = 0; r < actualKeyboard.length; r++) {
+ final int row = r + 1;
+ // Test both keyboards' rows have the same number of columns.
+ assertEquals(tag + " labels row=" + row
+ + "\nexpected=" + Arrays.toString(expectedKeyboard[r])
+ + "\nactual =" + ActualKeyboardBuilder.toString(actualKeyboard[r]),
+ expectedKeyboard[r].length, actualKeyboard[r].length);
+ for (int c = 0; c < actualKeyboard[r].length; c++) {
+ final int column = c + 1;
+ final Key actualKey = actualKeyboard[r][c];
+ final ExpectedKey expectedKey = expectedKeyboard[r][c];
+ // Test both keyboards' keys have the same visual outlook and key output.
+ assertTrue(tag + " labels row,column=" + row + "," + column
+ + "\nexpected=" + expectedKey
+ + "\nactual =" + ActualKeyboardBuilder.toString(actualKey),
+ expectedKey.equalsTo(actualKey));
+ }
+ }
+ }
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsEnglishUS.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsEnglishUS.java
new file mode 100644
index 000000000..0792a5789
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsEnglishUS.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout.tests;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.inputmethod.keyboard.layout.Qwerty;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
+
+import java.util.Locale;
+
+/**
+ * en_US: English (United States)/qwerty
+ */
+@SmallTest
+public final class TestsEnglishUS extends LayoutTestsBase {
+ @Override
+ Locale getTestLocale() {
+ return new Locale("en", "US");
+ }
+
+ @Override
+ String getTestKeyboardLayout() {
+ return "qwerty";
+ }
+
+ @Override
+ ExpectedKey[][] getAlphabet(final boolean isPhone) {
+ final ExpectedKey[][] keyboard = Qwerty.getAlphabet(isPhone);
+ final ExpectedKeyboardBuilder builder = new ExpectedKeyboardBuilder(keyboard);
+ setAccentedLetters(builder);
+ return builder.build();
+ }
+
+ static ExpectedKeyboardBuilder setAccentedLetters(final ExpectedKeyboardBuilder builder) {
+ return builder
+ // U+00E8: "è" LATIN SMALL LETTER E WITH GRAVE
+ // U+00E9: "é" LATIN SMALL LETTER E WITH ACUTE
+ // U+00EA: "ê" LATIN SMALL LETTER E WITH CIRCUMFLEX
+ // U+00EB: "ë" LATIN SMALL LETTER E WITH DIAERESIS
+ // U+0113: "ē" LATIN SMALL LETTER E WITH MACRON
+ .setMoreKeysOf("e", "3", "\u00E8", "\u00E9", "\u00EA", "\u00EB", "\u0113")
+ // U+00FB: "û" LATIN SMALL LETTER U WITH CIRCUMFLEX
+ // U+00FC: "ü" LATIN SMALL LETTER U WITH DIAERESIS
+ // U+00F9: "ù" LATIN SMALL LETTER U WITH GRAVE
+ // U+00FA: "ú" LATIN SMALL LETTER U WITH ACUTE
+ // U+016B: "ū" LATIN SMALL LETTER U WITH MACRON
+ .setMoreKeysOf("u", "7", "\u00FB", "\u00FC", "\u00F9", "\u00FA", "\u016B")
+ // U+00EE: "î" LATIN SMALL LETTER I WITH CIRCUMFLEX
+ // U+00EF: "ï" LATIN SMALL LETTER I WITH DIAERESIS
+ // U+00ED: "í" LATIN SMALL LETTER I WITH ACUTE
+ // U+012B: "ī" LATIN SMALL LETTER I WITH MACRON
+ // U+00EC: "ì" LATIN SMALL LETTER I WITH GRAVE
+ .setMoreKeysOf("i", "8", "\u00EE", "\u00EF", "\u00ED", "\u012B", "\u00EC")
+ // U+00F4: "ô" LATIN SMALL LETTER O WITH CIRCUMFLEX
+ // U+00F6: "ö" LATIN SMALL LETTER O WITH DIAERESIS
+ // U+00F2: "ò" LATIN SMALL LETTER O WITH GRAVE
+ // U+00F3: "ó" LATIN SMALL LETTER O WITH ACUTE
+ // U+0153: "œ" LATIN SMALL LIGATURE OE
+ // U+00F8: "ø" LATIN SMALL LETTER O WITH STROKE
+ // U+014D: "ō" LATIN SMALL LETTER O WITH MACRON
+ // U+00F5: "õ" LATIN SMALL LETTER O WITH TILDE
+ .setMoreKeysOf("o",
+ "9", "\u00F4", "\u00F6", "\u00F2", "\u00F3", "\u0153", "\u00F8", "\u014D",
+ "\u00F5")
+ // U+00E1: "á" LATIN SMALL LETTER A WITH ACUTE
+ // U+00E2: "â" LATIN SMALL LETTER A WITH CIRCUMFLEX
+ // U+00E4: "ä" LATIN SMALL LETTER A WITH DIAERESIS
+ // U+00E6: "æ" LATIN SMALL LETTER AE
+ // U+00E3: "ã" LATIN SMALL LETTER A WITH TILDE
+ // U+00E5: "å" LATIN SMALL LETTER A WITH RING ABOVE
+ // U+0101: "ā" LATIN SMALL LETTER A WITH MACRON
+ .setMoreKeysOf("a",
+ "\u00E0", "\u00E1", "\u00E2", "\u00E4", "\u00E6", "\u00E3", "\u00E5",
+ "\u0101")
+ // U+00DF: "ß" LATIN SMALL LETTER SHARP S
+ .setMoreKeysOf("s", "\u00DF")
+ // U+00E7: "ç" LATIN SMALL LETTER C WITH CEDILLA
+ .setMoreKeysOf("c", "\u00E7")
+ // U+00F1: "ñ" LATIN SMALL LETTER N WITH TILDE
+ .setMoreKeysOf("n", "\u00F1");
+ }
+}