diff options
Diffstat (limited to 'tests/src')
41 files changed, 4086 insertions, 1559 deletions
diff --git a/tests/src/com/android/inputmethod/keyboard/MoreKeysKeyboardBuilderFixedOrderTests.java b/tests/src/com/android/inputmethod/keyboard/MoreKeysKeyboardBuilderFixedOrderTests.java index 01814ae13..6d9c3fdbb 100644 --- a/tests/src/com/android/inputmethod/keyboard/MoreKeysKeyboardBuilderFixedOrderTests.java +++ b/tests/src/com/android/inputmethod/keyboard/MoreKeysKeyboardBuilderFixedOrderTests.java @@ -47,7 +47,7 @@ public class MoreKeysKeyboardBuilderFixedOrderTests extends AndroidTestCase { final int coordXInParent) { final MoreKeysKeyboardParams params = new MoreKeysKeyboardParams(); params.setParameters(numKeys, columnNum, WIDTH, HEIGHT, coordXInParent, KEYBOARD_WIDTH, - /* isFixedOrderColumn */true, /* dividerWidth */0); + true /* isFixedOrderColumn */, 0 /* dividerWidth */); return params; } diff --git a/tests/src/com/android/inputmethod/keyboard/MoreKeysKeyboardBuilderTests.java b/tests/src/com/android/inputmethod/keyboard/MoreKeysKeyboardBuilderTests.java index ce5573da9..b213721bd 100644 --- a/tests/src/com/android/inputmethod/keyboard/MoreKeysKeyboardBuilderTests.java +++ b/tests/src/com/android/inputmethod/keyboard/MoreKeysKeyboardBuilderTests.java @@ -47,7 +47,7 @@ public class MoreKeysKeyboardBuilderTests extends AndroidTestCase { final int coordXInParent) { final MoreKeysKeyboardParams params = new MoreKeysKeyboardParams(); params.setParameters(numKeys, maxColumns, WIDTH, HEIGHT, coordXInParent, KEYBOARD_WIDTH, - /* isFixedOrderColumn */false, /* dividerWidth */0); + false /* isFixedOrderColumn */, 0 /* dividerWidth */); return params; } diff --git a/tests/src/com/android/inputmethod/keyboard/SpacebarTextTests.java b/tests/src/com/android/inputmethod/keyboard/SpacebarTextTests.java deleted file mode 100644 index 850af94f7..000000000 --- a/tests/src/com/android/inputmethod/keyboard/SpacebarTextTests.java +++ /dev/null @@ -1,216 +0,0 @@ -/* - * Copyright (C) 2012 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.Context; -import android.content.res.Resources; -import android.test.AndroidTestCase; -import android.test.suitebuilder.annotation.SmallTest; -import android.view.inputmethod.InputMethodSubtype; - -import com.android.inputmethod.latin.AdditionalSubtype; -import com.android.inputmethod.latin.CollectionUtils; -import com.android.inputmethod.latin.RichInputMethodManager; -import com.android.inputmethod.latin.StringUtils; -import com.android.inputmethod.latin.SubtypeLocale; -import com.android.inputmethod.latin.LocaleUtils.RunInLocale; - -import java.util.ArrayList; -import java.util.Locale; - -@SmallTest -public class SpacebarTextTests extends AndroidTestCase { - // Locale to subtypes list. - private final ArrayList<InputMethodSubtype> mSubtypesList = CollectionUtils.newArrayList(); - - private RichInputMethodManager mRichImm; - private Resources mRes; - - InputMethodSubtype EN_US; - InputMethodSubtype EN_GB; - InputMethodSubtype ES_US; - InputMethodSubtype FR; - InputMethodSubtype FR_CA; - InputMethodSubtype DE; - InputMethodSubtype ZZ; - InputMethodSubtype DE_QWERTY; - InputMethodSubtype FR_QWERTZ; - InputMethodSubtype US_AZERTY; - InputMethodSubtype ZZ_AZERTY; - - @Override - protected void setUp() throws Exception { - super.setUp(); - final Context context = getContext(); - RichInputMethodManager.init(context); - mRichImm = RichInputMethodManager.getInstance(); - mRes = context.getResources(); - SubtypeLocale.init(context); - - EN_US = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet(Locale.US.toString(), "qwerty"); - EN_GB = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet(Locale.UK.toString(), "qwerty"); - ES_US = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet("es_US", "spanish"); - FR = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet(Locale.FRENCH.toString(), "azerty"); - FR_CA = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet( - Locale.CANADA_FRENCH.toString(), "qwerty"); - DE = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet(Locale.GERMAN.toString(), "qwertz"); - ZZ = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet(SubtypeLocale.NO_LANGUAGE, "qwerty"); - DE_QWERTY = AdditionalSubtype.createAdditionalSubtype( - Locale.GERMAN.toString(), "qwerty", null); - FR_QWERTZ = AdditionalSubtype.createAdditionalSubtype( - Locale.FRENCH.toString(), "qwertz", null); - US_AZERTY = AdditionalSubtype.createAdditionalSubtype( - Locale.US.toString(), "azerty", null); - ZZ_AZERTY = AdditionalSubtype.createAdditionalSubtype( - SubtypeLocale.NO_LANGUAGE, "azerty", null); - } - - public void testAllFullDisplayName() { - for (final InputMethodSubtype subtype : mSubtypesList) { - final String subtypeName = SubtypeLocale.getSubtypeDisplayName(subtype); - final String spacebarText = MainKeyboardView.getFullDisplayName(subtype); - final String languageName = - SubtypeLocale.getSubtypeLocaleDisplayName(subtype.getLocale()); - if (SubtypeLocale.isNoLanguage(subtype)) { - assertFalse(subtypeName, spacebarText.contains(languageName)); - } else { - assertTrue(subtypeName, spacebarText.contains(languageName)); - } - } - } - - public void testAllMiddleDisplayName() { - for (final InputMethodSubtype subtype : mSubtypesList) { - final String subtypeName = SubtypeLocale.getSubtypeDisplayName(subtype); - final String spacebarText = MainKeyboardView.getMiddleDisplayName(subtype); - if (SubtypeLocale.isNoLanguage(subtype)) { - assertEquals(subtypeName, - SubtypeLocale.getKeyboardLayoutSetName(subtype), spacebarText); - } else { - assertEquals(subtypeName, - SubtypeLocale.getSubtypeLocaleDisplayName(subtype.getLocale()), - spacebarText); - } - } - } - - public void testAllShortDisplayName() { - for (final InputMethodSubtype subtype : mSubtypesList) { - final String subtypeName = SubtypeLocale.getSubtypeDisplayName(subtype); - final Locale locale = SubtypeLocale.getSubtypeLocale(subtype); - final String spacebarText = MainKeyboardView.getShortDisplayName(subtype); - final String languageCode = StringUtils.capitalizeFirstCodePoint( - locale.getLanguage(), locale); - if (SubtypeLocale.isNoLanguage(subtype)) { - assertEquals(subtypeName, "", spacebarText); - } else { - assertEquals(subtypeName, languageCode, spacebarText); - } - } - } - - // InputMethodSubtype's display name for spacebar text in its locale. - // isAdditionalSubtype (T=true, F=false) - // locale layout | Short Middle Full - // ------ ------- - ---- --------- ---------------------- - // en_US qwerty F En English English (US) exception - // en_GB qwerty F En English English (UK) exception - // es_US spanish F Es Español Español (EE.UU.) exception - // fr azerty F Fr Français Français - // fr_CA qwerty F Fr Français Français (Canada) - // de qwertz F De Deutsch Deutsch - // zz qwerty F QWERTY QWERTY - // fr qwertz T Fr Français Français - // de qwerty T De Deutsch Deutsch - // en_US azerty T En English English (US) - // zz azerty T AZERTY AZERTY - - private final RunInLocale<Void> testsPredefinedSubtypes = new RunInLocale<Void>() { - @Override - protected Void job(Resources res) { - assertEquals("en_US", "English (US)", MainKeyboardView.getFullDisplayName(EN_US)); - assertEquals("en_GB", "English (UK)", MainKeyboardView.getFullDisplayName(EN_GB)); - assertEquals("es_US", "Español (EE.UU.)", MainKeyboardView.getFullDisplayName(ES_US)); - assertEquals("fr ", "Français", MainKeyboardView.getFullDisplayName(FR)); - assertEquals("fr_CA", "Français (Canada)", MainKeyboardView.getFullDisplayName(FR_CA)); - assertEquals("de ", "Deutsch", MainKeyboardView.getFullDisplayName(DE)); - assertEquals("zz ", "QWERTY", MainKeyboardView.getFullDisplayName(ZZ)); - - assertEquals("en_US", "English", MainKeyboardView.getMiddleDisplayName(EN_US)); - assertEquals("en_GB", "English", MainKeyboardView.getMiddleDisplayName(EN_GB)); - assertEquals("es_US", "Español", MainKeyboardView.getMiddleDisplayName(ES_US)); - assertEquals("fr ", "Français", MainKeyboardView.getMiddleDisplayName(FR)); - assertEquals("fr_CA", "Français", MainKeyboardView.getMiddleDisplayName(FR_CA)); - assertEquals("de ", "Deutsch", MainKeyboardView.getMiddleDisplayName(DE)); - assertEquals("zz ", "QWERTY", MainKeyboardView.getMiddleDisplayName(ZZ)); - - assertEquals("en_US", "En", MainKeyboardView.getShortDisplayName(EN_US)); - assertEquals("en_GB", "En", MainKeyboardView.getShortDisplayName(EN_GB)); - assertEquals("es_US", "Es", MainKeyboardView.getShortDisplayName(ES_US)); - assertEquals("fr ", "Fr", MainKeyboardView.getShortDisplayName(FR)); - assertEquals("fr_CA", "Fr", MainKeyboardView.getShortDisplayName(FR_CA)); - assertEquals("de ", "De", MainKeyboardView.getShortDisplayName(DE)); - assertEquals("zz ", "", MainKeyboardView.getShortDisplayName(ZZ)); - return null; - } - }; - - private final RunInLocale<Void> testsAdditionalSubtypes = new RunInLocale<Void>() { - @Override - protected Void job(Resources res) { - assertEquals("fr qwertz", "Français", - MainKeyboardView.getFullDisplayName(FR_QWERTZ)); - assertEquals("de qwerty", "Deutsch", - MainKeyboardView.getFullDisplayName(DE_QWERTY)); - assertEquals("en_US azerty", "English (US)", - MainKeyboardView.getFullDisplayName(US_AZERTY)); - assertEquals("zz azerty", "AZERTY", - MainKeyboardView.getFullDisplayName(ZZ_AZERTY)); - - assertEquals("fr qwertz", "Français", - MainKeyboardView.getMiddleDisplayName(FR_QWERTZ)); - assertEquals("de qwerty", "Deutsch", - MainKeyboardView.getMiddleDisplayName(DE_QWERTY)); - assertEquals("en_US azerty", "English", - MainKeyboardView.getMiddleDisplayName(US_AZERTY)); - assertEquals("zz azerty", "AZERTY", - MainKeyboardView.getMiddleDisplayName(ZZ_AZERTY)); - - assertEquals("fr qwertz", "Fr", MainKeyboardView.getShortDisplayName(FR_QWERTZ)); - assertEquals("de qwerty", "De", MainKeyboardView.getShortDisplayName(DE_QWERTY)); - assertEquals("en_US azerty", "En", MainKeyboardView.getShortDisplayName(US_AZERTY)); - assertEquals("zz azerty", "", MainKeyboardView.getShortDisplayName(ZZ_AZERTY)); - return null; - } - }; - - public void testPredefinedSubtypesInEnglish() { - testsPredefinedSubtypes.runInLocale(mRes, Locale.ENGLISH); - } - - public void testAdditionalSubtypeInEnglish() { - testsAdditionalSubtypes.runInLocale(mRes, Locale.ENGLISH); - } - - public void testPredefinedSubtypesInFrench() { - testsPredefinedSubtypes.runInLocale(mRes, Locale.FRENCH); - } - - public void testAdditionalSubtypeInFrench() { - testsAdditionalSubtypes.runInLocale(mRes, Locale.FRENCH); - } -} diff --git a/tests/src/com/android/inputmethod/keyboard/internal/KeySpecParserSplitTests.java b/tests/src/com/android/inputmethod/keyboard/internal/KeySpecParserSplitTests.java index eea1efc49..2eb448c82 100644 --- a/tests/src/com/android/inputmethod/keyboard/internal/KeySpecParserSplitTests.java +++ b/tests/src/com/android/inputmethod/keyboard/internal/KeySpecParserSplitTests.java @@ -22,8 +22,8 @@ import android.content.res.Resources; import android.test.InstrumentationTestCase; import android.test.suitebuilder.annotation.MediumTest; -import com.android.inputmethod.latin.CollectionUtils; -import com.android.inputmethod.latin.LocaleUtils.RunInLocale; +import com.android.inputmethod.latin.utils.CollectionUtils; +import com.android.inputmethod.latin.utils.RunInLocale; import java.lang.reflect.Field; import java.util.ArrayList; @@ -51,9 +51,10 @@ public class KeySpecParserSplitTests extends InstrumentationTestCase { }.runInLocale(targetContext.getResources(), TEST_LOCALE); final String[] testResourceNames = getAllResourceIdNames( com.android.inputmethod.latin.tests.R.string.class); - mTextsSet.loadStringResourcesInternal(instrumentation.getContext(), - testResourceNames, - com.android.inputmethod.latin.tests.R.string.empty_string); + mTextsSet.loadStringResourcesInternal(instrumentation.getContext(), testResourceNames, + // This dummy raw resource is needed to be able to load string resources from a test + // APK successfully. + com.android.inputmethod.latin.tests.R.raw.dummy_resource_for_testing); } private static String[] getAllResourceIdNames(final Class<?> resourceIdClass) { diff --git a/tests/src/com/android/inputmethod/keyboard/internal/KeySpecParserTests.java b/tests/src/com/android/inputmethod/keyboard/internal/KeySpecParserTests.java index b55158d3e..afb2b0343 100644 --- a/tests/src/com/android/inputmethod/keyboard/internal/KeySpecParserTests.java +++ b/tests/src/com/android/inputmethod/keyboard/internal/KeySpecParserTests.java @@ -26,7 +26,7 @@ import android.test.AndroidTestCase; import android.test.suitebuilder.annotation.SmallTest; import com.android.inputmethod.latin.Constants; -import com.android.inputmethod.latin.LocaleUtils.RunInLocale; +import com.android.inputmethod.latin.utils.RunInLocale; import java.util.Arrays; import java.util.Locale; diff --git a/tests/src/com/android/inputmethod/keyboard/internal/KeyboardStateMultiTouchTests.java b/tests/src/com/android/inputmethod/keyboard/internal/KeyboardStateMultiTouchTests.java index b193e66dc..9ad81c01d 100644 --- a/tests/src/com/android/inputmethod/keyboard/internal/KeyboardStateMultiTouchTests.java +++ b/tests/src/com/android/inputmethod/keyboard/internal/KeyboardStateMultiTouchTests.java @@ -60,7 +60,7 @@ public class KeyboardStateMultiTouchTests extends KeyboardStateTestsBase { // Chording input in shift locked. public void testChordingShiftLocked() { // Long press shift key, enter alphabet shift locked. - longPressAndReleaseKey(CODE_SHIFT, ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED, + longPressAndReleaseShiftKey(ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED, ALPHABET_SHIFT_LOCKED); // Press shift key and hold, enter into choring shift state. @@ -119,7 +119,7 @@ public class KeyboardStateMultiTouchTests extends KeyboardStateTestsBase { // Load keyboard loadKeyboard(ALPHABET_UNSHIFTED); // Long press shift key, enter alphabet shift locked. - longPressAndReleaseKey(CODE_SHIFT, ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED, + longPressAndReleaseShiftKey(ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED, ALPHABET_SHIFT_LOCKED); // Press/release "?123" key, enter into symbols. pressAndReleaseKey(CODE_SYMBOL, SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED); @@ -137,7 +137,7 @@ public class KeyboardStateMultiTouchTests extends KeyboardStateTestsBase { // Load keyboard loadKeyboard(ALPHABET_UNSHIFTED); // Long press shift key, enter alphabet shift locked. - longPressAndReleaseKey(CODE_SHIFT, ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED, + longPressAndReleaseShiftKey(ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED, ALPHABET_SHIFT_LOCKED); // Press/release "?123" key, enter into symbols. pressAndReleaseKey(CODE_SYMBOL, SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED); @@ -196,7 +196,7 @@ public class KeyboardStateMultiTouchTests extends KeyboardStateTestsBase { // Load keyboard loadKeyboard(ALPHABET_UNSHIFTED); // Long press shift key, enter alphabet shift locked. - longPressAndReleaseKey(CODE_SHIFT, ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED, + longPressAndReleaseShiftKey(ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED, ALPHABET_SHIFT_LOCKED); // Press/release "?123" key, enter into symbols. pressAndReleaseKey(CODE_SYMBOL, SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED); @@ -216,7 +216,7 @@ public class KeyboardStateMultiTouchTests extends KeyboardStateTestsBase { // Load keyboard loadKeyboard(ALPHABET_UNSHIFTED); // Long press shift key, enter alphabet shift locked. - longPressAndReleaseKey(CODE_SHIFT, ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED, + longPressAndReleaseShiftKey(ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED, ALPHABET_SHIFT_LOCKED); // Press/release "?123" key, enter into symbols. pressAndReleaseKey(CODE_SYMBOL, SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED); @@ -397,29 +397,29 @@ public class KeyboardStateMultiTouchTests extends KeyboardStateTestsBase { public void testLongPressShiftAndChording() { // Long press shift key, enter maybe shift locked. - longPressKey(CODE_SHIFT, ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED); + longPressShiftKey(ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED); // Press/release letter key, remain in manual shifted. chordingPressAndReleaseKey('A', ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED); // Release shift key, back to alphabet (not shift locked). releaseKey(CODE_SHIFT, ALPHABET_UNSHIFTED); // Long press shift key, enter alphabet shift locked. - longPressAndReleaseKey(CODE_SHIFT, ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED, + longPressAndReleaseShiftKey(ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED, ALPHABET_SHIFT_LOCKED); // Long press shift key, enter maybe alphabet. - longPressKey(CODE_SHIFT, ALPHABET_SHIFT_LOCK_SHIFTED, ALPHABET_SHIFT_LOCK_SHIFTED); + longPressShiftKey(ALPHABET_SHIFT_LOCK_SHIFTED, ALPHABET_SHIFT_LOCK_SHIFTED); // Press/release letter key, remain in manual shifted. chordingPressAndReleaseKey('A', ALPHABET_SHIFT_LOCK_SHIFTED, ALPHABET_SHIFT_LOCK_SHIFTED); // Release shift key, back to shift locked (not alphabet). releaseKey(CODE_SHIFT, ALPHABET_SHIFT_LOCKED); // Long press shift key, enter alphabet - longPressAndReleaseKey(CODE_SHIFT, ALPHABET_SHIFT_LOCK_SHIFTED, ALPHABET_SHIFT_LOCK_SHIFTED, + longPressAndReleaseShiftKey(ALPHABET_SHIFT_LOCK_SHIFTED, ALPHABET_SHIFT_LOCK_SHIFTED, ALPHABET_UNSHIFTED); // Press/release shift key, enter alphabet shifted. pressAndReleaseKey(CODE_SHIFT, ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED); // Long press shift key, enter maybe alphabet. - longPressKey(CODE_SHIFT, ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED); + longPressShiftKey(ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED); // Press/release letter key, remain in manual shifted. chordingPressAndReleaseKey('A', ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED); // Release shift key, back to alphabet shifted (not alphabet). @@ -430,7 +430,7 @@ public class KeyboardStateMultiTouchTests extends KeyboardStateTestsBase { // Load keyboard, should be in automatic shifted. loadKeyboard(ALPHABET_AUTOMATIC_SHIFTED); // Long press shift key, enter maybe shift locked. - longPressKey(CODE_SHIFT, ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED); + longPressShiftKey(ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED); // Press/release letter key, remain in manual shifted. chordingPressAndReleaseKey('A', ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED); // Release shift key, back to alphabet (not shift locked). @@ -449,7 +449,7 @@ public class KeyboardStateMultiTouchTests extends KeyboardStateTestsBase { // releaseKey(CODE_SHIFT, ALPHABET_MANUAL_SHIFTED); // // // Long press shift key, enter alphabet shift locked. -// longPressAndReleaseKey(CODE_SHIFT, ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED, +// longPressAndReleaseShiftKey(ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED, // ALPHABET_SHIFT_LOCKED); // // First shift key tap. // pressAndReleaseKey(CODE_SHIFT, ALPHABET_MANUAL_SHIFTED, ALPHABET_UNSHIFTED); diff --git a/tests/src/com/android/inputmethod/keyboard/internal/KeyboardStateSingleTouchTests.java b/tests/src/com/android/inputmethod/keyboard/internal/KeyboardStateSingleTouchTests.java index d5b9d1dfd..c7ac76d93 100644 --- a/tests/src/com/android/inputmethod/keyboard/internal/KeyboardStateSingleTouchTests.java +++ b/tests/src/com/android/inputmethod/keyboard/internal/KeyboardStateSingleTouchTests.java @@ -91,7 +91,7 @@ public class KeyboardStateSingleTouchTests extends KeyboardStateTestsBase { // Switching between alphabet shift locked and symbols. public void testAlphabetShiftLockedAndSymbols() { // Long press shift key, enter alphabet shift locked. - longPressAndReleaseKey(CODE_SHIFT, ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED, + longPressAndReleaseShiftKey(ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED, ALPHABET_SHIFT_LOCKED); // Press/release "?123" key, enter into symbols. @@ -133,7 +133,7 @@ public class KeyboardStateSingleTouchTests extends KeyboardStateTestsBase { // Automatic switch back to alphabet shift locked test by space key. public void testSwitchBackBySpaceShiftLocked() { // Long press shift key, enter alphabet shift locked. - longPressAndReleaseKey(CODE_SHIFT, ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED, + longPressAndReleaseShiftKey(ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED, ALPHABET_SHIFT_LOCKED); // Press/release "?123" key, enter into symbols. @@ -196,13 +196,13 @@ public class KeyboardStateSingleTouchTests extends KeyboardStateTestsBase { // Load keyboard, should be in alphabet. loadKeyboard(ALPHABET_UNSHIFTED); // Long press shift key, enter alphabet shift locked. - longPressAndReleaseKey(CODE_SHIFT, ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED, + longPressAndReleaseShiftKey(ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED, ALPHABET_SHIFT_LOCKED); // Press/release shift key, back to alphabet. pressAndReleaseKey(CODE_SHIFT, ALPHABET_SHIFT_LOCK_SHIFTED, ALPHABET_UNSHIFTED); // Long press shift key, enter alphabet shift locked. - longPressAndReleaseKey(CODE_SHIFT, ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED, + longPressAndReleaseShiftKey(ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED, ALPHABET_SHIFT_LOCKED); // Press/release letter key, remain in shift locked. pressAndReleaseKey('A', ALPHABET_SHIFT_LOCKED, ALPHABET_SHIFT_LOCKED); @@ -212,16 +212,16 @@ public class KeyboardStateSingleTouchTests extends KeyboardStateTestsBase { pressAndReleaseKey(CODE_SHIFT, ALPHABET_SHIFT_LOCK_SHIFTED, ALPHABET_UNSHIFTED); // Long press shift key, enter alphabet shift locked. - longPressAndReleaseKey(CODE_SHIFT, ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED, + longPressAndReleaseShiftKey(ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED, ALPHABET_SHIFT_LOCKED); // Long press shift key, back to alphabet. - longPressAndReleaseKey(CODE_SHIFT, ALPHABET_SHIFT_LOCK_SHIFTED, ALPHABET_SHIFT_LOCK_SHIFTED, + longPressAndReleaseShiftKey(ALPHABET_SHIFT_LOCK_SHIFTED, ALPHABET_SHIFT_LOCK_SHIFTED, ALPHABET_UNSHIFTED); // Press/release shift key, enter alphabet shifted. pressAndReleaseKey(CODE_SHIFT, ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED); // Long press shift key, enter alphabet shift locked. - longPressAndReleaseKey(CODE_SHIFT, ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED, + longPressAndReleaseShiftKey(ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED, ALPHABET_SHIFT_LOCKED); // Press/release shift key, back to alphabet. pressAndReleaseKey(CODE_SHIFT, ALPHABET_SHIFT_LOCK_SHIFTED, ALPHABET_UNSHIFTED); @@ -231,7 +231,7 @@ public class KeyboardStateSingleTouchTests extends KeyboardStateTestsBase { // Load keyboard, should be in automatic shifted. loadKeyboard(ALPHABET_AUTOMATIC_SHIFTED); // Long press shift key, enter alphabet shift locked. - longPressAndReleaseKey(CODE_SHIFT, ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED, + longPressAndReleaseShiftKey(ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED, ALPHABET_SHIFT_LOCKED); // Press/release shift key, back to alphabet. pressAndReleaseKey(CODE_SHIFT, ALPHABET_SHIFT_LOCK_SHIFTED, ALPHABET_UNSHIFTED); @@ -293,12 +293,12 @@ public class KeyboardStateSingleTouchTests extends KeyboardStateTestsBase { updateShiftState(ALPHABET_UNSHIFTED); // Long press shift key, enter alphabet shift locked. - longPressAndReleaseKey(CODE_SHIFT, ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED, + longPressAndReleaseShiftKey(ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED, ALPHABET_SHIFT_LOCKED); // Update shift state, remained in alphabet shift locked. updateShiftState(ALPHABET_SHIFT_LOCKED); // Long press shift key, back to alphabet. - longPressAndReleaseKey(CODE_SHIFT, ALPHABET_SHIFT_LOCK_SHIFTED, ALPHABET_SHIFT_LOCK_SHIFTED, + longPressAndReleaseShiftKey(ALPHABET_SHIFT_LOCK_SHIFTED, ALPHABET_SHIFT_LOCK_SHIFTED, ALPHABET_UNSHIFTED); // Press/release "?123" key, enter into symbols. @@ -326,12 +326,12 @@ public class KeyboardStateSingleTouchTests extends KeyboardStateTestsBase { updateShiftState(ALPHABET_AUTOMATIC_SHIFTED); // Long press shift key, enter alphabet shift locked. - longPressAndReleaseKey(CODE_SHIFT, ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED, + longPressAndReleaseShiftKey(ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED, ALPHABET_SHIFT_LOCKED); // Update shift state, remained in alphabet shift locked (not automatic shifted). updateShiftState(ALPHABET_SHIFT_LOCKED); // Long press shift key, back to alphabet. - longPressAndReleaseKey(CODE_SHIFT, ALPHABET_SHIFT_LOCK_SHIFTED, ALPHABET_SHIFT_LOCK_SHIFTED, + longPressAndReleaseShiftKey(ALPHABET_SHIFT_LOCK_SHIFTED, ALPHABET_SHIFT_LOCK_SHIFTED, ALPHABET_UNSHIFTED); // Load keyboard, should be in automatic shifted. @@ -383,7 +383,7 @@ public class KeyboardStateSingleTouchTests extends KeyboardStateTestsBase { // Alphabet shift locked -> shift key + letter -> alphabet shift locked. // Long press shift key, enter alphabet shift locked. - longPressAndReleaseKey(CODE_SHIFT, ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED, + longPressAndReleaseShiftKey(ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED, ALPHABET_SHIFT_LOCKED); // Press and slide from "123?" key, enter symbols. pressAndSlideFromKey(CODE_SYMBOL, SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED); @@ -441,7 +441,7 @@ public class KeyboardStateSingleTouchTests extends KeyboardStateTestsBase { // Alphabet shift locked -> shift key + letter -> cancel -> alphabet shift locked. // Long press shift key, enter alphabet shift locked. - longPressAndReleaseKey(CODE_SHIFT, ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED, + longPressAndReleaseShiftKey(ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED, ALPHABET_SHIFT_LOCKED); // Press and slide from "123?" key, enter symbols. pressAndSlideFromKey(CODE_SYMBOL, SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED); @@ -500,7 +500,7 @@ public class KeyboardStateSingleTouchTests extends KeyboardStateTestsBase { // Load keyboard loadKeyboard(ALPHABET_UNSHIFTED); // Long press shift key, enter alphabet shift locked. - longPressAndReleaseKey(CODE_SHIFT, ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED, + longPressAndReleaseShiftKey(ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED, ALPHABET_SHIFT_LOCKED); // Press/release "?123" key, enter into symbols. pressAndReleaseKey(CODE_SYMBOL, SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED); @@ -517,7 +517,7 @@ public class KeyboardStateSingleTouchTests extends KeyboardStateTestsBase { // Load keyboard loadKeyboard(ALPHABET_UNSHIFTED); // Long press shift key, enter alphabet shift locked. - longPressAndReleaseKey(CODE_SHIFT, ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED, + longPressAndReleaseShiftKey(ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED, ALPHABET_SHIFT_LOCKED); // Press/release "?123" key, enter into symbols. pressAndReleaseKey(CODE_SYMBOL, SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED); @@ -574,7 +574,7 @@ public class KeyboardStateSingleTouchTests extends KeyboardStateTestsBase { // Load keyboard loadKeyboard(ALPHABET_UNSHIFTED); // Long press shift key, enter alphabet shift locked. - longPressAndReleaseKey(CODE_SHIFT, ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED, + longPressAndReleaseShiftKey(ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED, ALPHABET_SHIFT_LOCKED); // Press/release "?123" key, enter into symbols. pressAndReleaseKey(CODE_SYMBOL, SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED); @@ -592,7 +592,7 @@ public class KeyboardStateSingleTouchTests extends KeyboardStateTestsBase { // Load keyboard loadKeyboard(ALPHABET_UNSHIFTED); // Long press shift key, enter alphabet shift locked. - longPressAndReleaseKey(CODE_SHIFT, ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED, + longPressAndReleaseShiftKey(ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED, ALPHABET_SHIFT_LOCKED); // Press/release "?123" key, enter into symbols. pressAndReleaseKey(CODE_SYMBOL, SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED); @@ -651,7 +651,7 @@ public class KeyboardStateSingleTouchTests extends KeyboardStateTestsBase { // Load keyboard loadKeyboard(ALPHABET_UNSHIFTED); // Long press shift key, enter alphabet shift locked. - longPressAndReleaseKey(CODE_SHIFT, ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED, + longPressAndReleaseShiftKey(ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED, ALPHABET_SHIFT_LOCKED); // Press/release "?123" key, enter into symbols. pressAndReleaseKey(CODE_SYMBOL, SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED); @@ -670,7 +670,7 @@ public class KeyboardStateSingleTouchTests extends KeyboardStateTestsBase { // Load keyboard loadKeyboard(ALPHABET_UNSHIFTED); // Long press shift key, enter alphabet shift locked. - longPressAndReleaseKey(CODE_SHIFT, ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED, + longPressAndReleaseShiftKey(ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED, ALPHABET_SHIFT_LOCKED); // Press/release "?123" key, enter into symbols. pressAndReleaseKey(CODE_SYMBOL, SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED); @@ -733,7 +733,7 @@ public class KeyboardStateSingleTouchTests extends KeyboardStateTestsBase { // Load keyboard loadKeyboard(ALPHABET_UNSHIFTED); // Long press shift key, enter alphabet shift locked. - longPressAndReleaseKey(CODE_SHIFT, ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED, + longPressAndReleaseShiftKey(ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED, ALPHABET_SHIFT_LOCKED); // Press/release "?123" key, enter into symbols. pressAndReleaseKey(CODE_SYMBOL, SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED); @@ -753,7 +753,7 @@ public class KeyboardStateSingleTouchTests extends KeyboardStateTestsBase { // Load keyboard loadKeyboard(ALPHABET_UNSHIFTED); // Long press shift key, enter alphabet shift locked. - longPressAndReleaseKey(CODE_SHIFT, ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED, + longPressAndReleaseShiftKey(ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED, ALPHABET_SHIFT_LOCKED); // Press/release "?123" key, enter into symbols. pressAndReleaseKey(CODE_SYMBOL, SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED); @@ -777,7 +777,7 @@ public class KeyboardStateSingleTouchTests extends KeyboardStateTestsBase { loadKeyboard(ALPHABET_UNSHIFTED); // Long press shift key, enter alphabet shift locked. - longPressAndReleaseKey(CODE_SHIFT, ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED, + longPressAndReleaseShiftKey(ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED, ALPHABET_SHIFT_LOCKED); // Change focus to new text field. loadKeyboard(ALPHABET_UNSHIFTED); @@ -808,7 +808,7 @@ public class KeyboardStateSingleTouchTests extends KeyboardStateTestsBase { loadKeyboard(ALPHABET_AUTOMATIC_SHIFTED); // Long press shift key, enter alphabet shift locked. - longPressAndReleaseKey(CODE_SHIFT, ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED, + longPressAndReleaseShiftKey(ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED, ALPHABET_SHIFT_LOCKED); // Change focus to new text field. loadKeyboard(ALPHABET_AUTOMATIC_SHIFTED); @@ -852,7 +852,7 @@ public class KeyboardStateSingleTouchTests extends KeyboardStateTestsBase { // Alphabet shift locked -> rotate -> alphabet shift locked. // Long press shift key, enter alphabet shift locked. - longPressAndReleaseKey(CODE_SHIFT, ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED, + longPressAndReleaseShiftKey(ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED, ALPHABET_SHIFT_LOCKED); // Rotate device, remain in alphabet shift locked. rotateDevice(ALPHABET_SHIFT_LOCKED); @@ -936,7 +936,7 @@ public class KeyboardStateSingleTouchTests extends KeyboardStateTestsBase { secondPressAndReleaseKey('J', ALPHABET_MANUAL_SHIFTED, ALPHABET_UNSHIFTED); // Long press shift key to enter alphabet shift locked. - longPressAndReleaseKey(CODE_SHIFT, ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED, + longPressAndReleaseShiftKey(ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED, ALPHABET_SHIFT_LOCKED); // Press/release shift key pressAndReleaseKey(CODE_SHIFT, ALPHABET_SHIFT_LOCK_SHIFTED, ALPHABET_UNSHIFTED); diff --git a/tests/src/com/android/inputmethod/keyboard/internal/KeyboardStateTestsBase.java b/tests/src/com/android/inputmethod/keyboard/internal/KeyboardStateTestsBase.java index e06ca064a..3ffd0a96a 100644 --- a/tests/src/com/android/inputmethod/keyboard/internal/KeyboardStateTestsBase.java +++ b/tests/src/com/android/inputmethod/keyboard/internal/KeyboardStateTestsBase.java @@ -18,6 +18,8 @@ package com.android.inputmethod.keyboard.internal; import android.test.AndroidTestCase; +import com.android.inputmethod.latin.Constants; + public class KeyboardStateTestsBase extends AndroidTestCase implements MockKeyboardSwitcher.MockConstants { protected MockKeyboardSwitcher mSwitcher; @@ -32,6 +34,11 @@ public class KeyboardStateTestsBase extends AndroidTestCase loadKeyboard(ALPHABET_UNSHIFTED); } + /** + * Set auto caps mode. + * + * @param autoCaps the auto cap mode. + */ public void setAutoCapsMode(final int autoCaps) { mSwitcher.setAutoCapsMode(autoCaps); } @@ -42,17 +49,32 @@ public class KeyboardStateTestsBase extends AndroidTestCase expected == actual); } + /** + * Emulate update keyboard shift state. + * + * @param afterUpdate the keyboard state after updating the keyboard shift state. + */ public void updateShiftState(final int afterUpdate) { mSwitcher.updateShiftState(); assertLayout("afterUpdate", afterUpdate, mSwitcher.getLayoutId()); } + /** + * Emulate load default keyboard. + * + * @param afterLoad the keyboard state after loading default keyboard. + */ public void loadKeyboard(final int afterLoad) { mSwitcher.loadKeyboard(); mSwitcher.updateShiftState(); assertLayout("afterLoad", afterLoad, mSwitcher.getLayoutId()); } + /** + * Emulate rotate device. + * + * @param afterRotate the keyboard state after rotating device. + */ public void rotateDevice(final int afterRotate) { mSwitcher.saveKeyboardState(); mSwitcher.loadKeyboard(); @@ -65,45 +87,97 @@ public class KeyboardStateTestsBase extends AndroidTestCase assertLayout("afterPress", afterPress, mSwitcher.getLayoutId()); } + /** + * Emulate key press. + * + * @param code the key code to press. + * @param afterPress the keyboard state after pressing the key. + */ public void pressKey(final int code, final int afterPress) { mSwitcher.expireDoubleTapTimeout(); pressKeyWithoutTimerExpire(code, true, afterPress); } + /** + * Emulate key release and register. + * + * @param code the key code to release and register + * @param afterRelease the keyboard state after releasing the key. + */ public void releaseKey(final int code, final int afterRelease) { mSwitcher.onCodeInput(code); mSwitcher.onReleaseKey(code, NOT_SLIDING); assertLayout("afterRelease", afterRelease, mSwitcher.getLayoutId()); } + /** + * Emulate key press and release. + * + * @param code the key code to press and release. + * @param afterPress the keyboard state after pressing the key. + * @param afterRelease the keyboard state after releasing the key. + */ public void pressAndReleaseKey(final int code, final int afterPress, final int afterRelease) { pressKey(code, afterPress); releaseKey(code, afterRelease); } + /** + * Emulate chording key press. + * + * @param code the chording key code. + * @param afterPress the keyboard state after pressing chording key. + */ public void chordingPressKey(final int code, final int afterPress) { mSwitcher.expireDoubleTapTimeout(); pressKeyWithoutTimerExpire(code, false, afterPress); } + /** + * Emulate chording key release. + * + * @param code the cording key code. + * @param afterRelease the keyboard state after releasing chording key. + */ public void chordingReleaseKey(final int code, final int afterRelease) { mSwitcher.onCodeInput(code); mSwitcher.onReleaseKey(code, NOT_SLIDING); assertLayout("afterRelease", afterRelease, mSwitcher.getLayoutId()); } + /** + * Emulate chording key press and release. + * + * @param code the chording key code. + * @param afterPress the keyboard state after pressing chording key. + * @param afterRelease the keyboard state after releasing chording key. + */ public void chordingPressAndReleaseKey(final int code, final int afterPress, final int afterRelease) { chordingPressKey(code, afterPress); chordingReleaseKey(code, afterRelease); } + /** + * Emulate start of the sliding key input. + * + * @param code the key code to start sliding. + * @param afterPress the keyboard state after pressing the key. + * @param afterSlide the keyboard state after releasing the key with sliding input. + */ public void pressAndSlideFromKey(final int code, final int afterPress, final int afterSlide) { pressKey(code, afterPress); mSwitcher.onReleaseKey(code, SLIDING); assertLayout("afterSlide", afterSlide, mSwitcher.getLayoutId()); } + /** + * Emulate end of the sliding key input. + * + * @param code the key code to stop sliding. + * @param afterPress the keyboard state after pressing the key. + * @param afterSlide the keyboard state after releasing the key and stop sliding. + */ public void stopSlidingOnKey(final int code, final int afterPress, final int afterSlide) { pressKey(code, afterPress); mSwitcher.onCodeInput(code); @@ -112,28 +186,67 @@ public class KeyboardStateTestsBase extends AndroidTestCase assertLayout("afterSlide", afterSlide, mSwitcher.getLayoutId()); } + /** + * Emulate cancel the sliding key input. + * + * @param afterCancelSliding the keyboard state after canceling sliding input. + */ public void stopSlidingAndCancel(final int afterCancelSliding) { mSwitcher.onFinishSlidingInput(); assertLayout("afterCancelSliding", afterCancelSliding, mSwitcher.getLayoutId()); } - public void longPressKey(final int code, final int afterPress, final int afterLongPress) { - pressKey(code, afterPress); - mSwitcher.onLongPressTimeout(code); + /** + * Emulate long press shift key. + * + * @param afterPress the keyboard state after pressing shift key. + * @param afterLongPress the keyboard state after long press fired. + */ + public void longPressShiftKey(final int afterPress, final int afterLongPress) { + // Long press shift key will register {@link Constants#CODE_CAPS_LOCK}. See + // {@link R.xml#key_styles_common} and its baseForShiftKeyStyle. We thus emulate the + // behavior that is implemented in {@link MainKeyboardView#onLongPress(PointerTracker)}. + pressKey(Constants.CODE_SHIFT, afterPress); + mSwitcher.onPressKey(Constants.CODE_CAPSLOCK, true /* isSinglePointer */); + mSwitcher.onCodeInput(Constants.CODE_CAPSLOCK); assertLayout("afterLongPress", afterLongPress, mSwitcher.getLayoutId()); } - public void longPressAndReleaseKey(final int code, final int afterPress, - final int afterLongPress, final int afterRelease) { - longPressKey(code, afterPress, afterLongPress); - releaseKey(code, afterRelease); - } - - public void secondPressKey(int code, int afterPress) { + /** + * Emulate long press shift key and release. + * + * @param afterPress the keyboard state after pressing shift key. + * @param afterLongPress the keyboard state after long press fired. + * @param afterRelease the keyboard state after shift key is released. + */ + public void longPressAndReleaseShiftKey(final int afterPress, final int afterLongPress, + final int afterRelease) { + // Long press shift key will register {@link Constants#CODE_CAPS_LOCK}. See + // {@link R.xml#key_styles_common} and its baseForShiftKeyStyle. We thus emulate the + // behavior that is implemented in {@link MainKeyboardView#onLongPress(PointerTracker)}. + longPressShiftKey(afterPress, afterLongPress); + releaseKey(Constants.CODE_CAPSLOCK, afterRelease); + } + + /** + * Emulate the second press of the double tap. + * + * @param code the key code to double tap. + * @param afterPress the keyboard state after pressing the second tap. + */ + public void secondPressKey(final int code, final int afterPress) { pressKeyWithoutTimerExpire(code, true, afterPress); } - public void secondPressAndReleaseKey(int code, int afterPress, int afterRelease) { + /** + * Emulate the second tap of the double tap. + * + * @param code the key code to double tap. + * @param afterPress the keyboard state after pressing the second tap. + * @param afterRelease the keyboard state after releasing the second tap. + */ + public void secondPressAndReleaseKey(final int code, final int afterPress, + final int afterRelease) { secondPressKey(code, afterPress); releaseKey(code, afterRelease); } diff --git a/tests/src/com/android/inputmethod/keyboard/internal/MockKeyboardSwitcher.java b/tests/src/com/android/inputmethod/keyboard/internal/MockKeyboardSwitcher.java index 90dbaabc9..6e3e37add 100644 --- a/tests/src/com/android/inputmethod/keyboard/internal/MockKeyboardSwitcher.java +++ b/tests/src/com/android/inputmethod/keyboard/internal/MockKeyboardSwitcher.java @@ -19,7 +19,7 @@ package com.android.inputmethod.keyboard.internal; import android.text.TextUtils; import com.android.inputmethod.latin.Constants; -import com.android.inputmethod.latin.RecapitalizeStatus; +import com.android.inputmethod.latin.utils.RecapitalizeStatus; public class MockKeyboardSwitcher implements KeyboardState.SwitchActions { public interface MockConstants { @@ -120,6 +120,11 @@ public class MockKeyboardSwitcher implements KeyboardState.SwitchActions { } @Override + public void setEmojiKeyboard() { + // Just ignore. + } + + @Override public void requestUpdatingShiftState() { mState.onUpdateShiftState(mAutoCapsState, RecapitalizeStatus.NOT_A_RECAPITALIZE_MODE); } @@ -139,24 +144,6 @@ public class MockKeyboardSwitcher implements KeyboardState.SwitchActions { return mIsInDoubleTapShiftKeyTimeout; } - @Override - public void startLongPressTimer(final int code) { - mLongPressTimeoutCode = code; - } - - @Override - public void cancelLongPressTimer() { - mLongPressTimeoutCode = 0; - } - - public void onLongPressTimeout(final int code) { - // TODO: Handle simultaneous long presses. - if (mLongPressTimeoutCode == code) { - mLongPressTimeoutCode = 0; - mState.onLongPressTimeout(code); - } - } - public void updateShiftState() { mState.onUpdateShiftState(mAutoCapsState, RecapitalizeStatus.NOT_A_RECAPITALIZE_MODE); } diff --git a/tests/src/com/android/inputmethod/latin/BinaryDictionaryTests.java b/tests/src/com/android/inputmethod/latin/BinaryDictionaryTests.java new file mode 100644 index 000000000..7ed3ee180 --- /dev/null +++ b/tests/src/com/android/inputmethod/latin/BinaryDictionaryTests.java @@ -0,0 +1,628 @@ +/* + * Copyright (C) 2013 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.latin; + +import android.test.AndroidTestCase; +import android.test.suitebuilder.annotation.LargeTest; +import android.util.Pair; + +import com.android.inputmethod.latin.makedict.CodePointUtils; +import com.android.inputmethod.latin.makedict.FormatSpec; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; +import java.util.Random; + +@LargeTest +public class BinaryDictionaryTests extends AndroidTestCase { + private static final String TEST_DICT_FILE_EXTENSION = ".testDict"; + private static final String TEST_LOCALE = "test"; + + @Override + protected void setUp() throws Exception { + super.setUp(); + } + + @Override + protected void tearDown() throws Exception { + super.tearDown(); + } + + private File createEmptyDictionaryAndGetFile(final String filename) throws IOException { + final File file = File.createTempFile(filename, TEST_DICT_FILE_EXTENSION, + getContext().getCacheDir()); + Map<String, String> attributeMap = new HashMap<String, String>(); + attributeMap.put(FormatSpec.FileHeader.SUPPORTS_DYNAMIC_UPDATE_ATTRIBUTE, + FormatSpec.FileHeader.ATTRIBUTE_VALUE_TRUE); + if (BinaryDictionary.createEmptyDictFile(file.getAbsolutePath(), + 3 /* dictVersion */, attributeMap)) { + return file; + } else { + throw new IOException("Empty dictionary cannot be created."); + } + } + + public void testIsValidDictionary() { + File dictFile = null; + try { + dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary"); + } catch (IOException e) { + fail("IOException while writing an initial dictionary : " + e); + } + BinaryDictionary binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(), + 0 /* offset */, dictFile.length(), true /* useFullEditDistance */, + Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */); + assertTrue("binaryDictionary must be valid for existing valid dictionary file.", + binaryDictionary.isValidDictionary()); + binaryDictionary.close(); + assertFalse("binaryDictionary must be invalid after closing.", + binaryDictionary.isValidDictionary()); + dictFile.delete(); + binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(), 0 /* offset */, + dictFile.length(), true /* useFullEditDistance */, Locale.getDefault(), + TEST_LOCALE, true /* isUpdatable */); + assertFalse("binaryDictionary must be invalid for not existing dictionary file.", + binaryDictionary.isValidDictionary()); + binaryDictionary.close(); + } + + public void testAddUnigramWord() { + File dictFile = null; + try { + dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary"); + } catch (IOException e) { + fail("IOException while writing an initial dictionary : " + e); + } + BinaryDictionary binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(), + 0 /* offset */, dictFile.length(), true /* useFullEditDistance */, + Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */); + + final int probability = 100; + binaryDictionary.addUnigramWord("aaa", probability); + // Reallocate and create. + binaryDictionary.addUnigramWord("aab", probability); + // Insert into children. + binaryDictionary.addUnigramWord("aac", probability); + // Make terminal. + binaryDictionary.addUnigramWord("aa", probability); + // Create children. + binaryDictionary.addUnigramWord("aaaa", probability); + // Reallocate and make termianl. + binaryDictionary.addUnigramWord("a", probability); + + final int updatedProbability = 200; + // Update. + binaryDictionary.addUnigramWord("aaa", updatedProbability); + + assertEquals(probability, binaryDictionary.getFrequency("aab")); + assertEquals(probability, binaryDictionary.getFrequency("aac")); + assertEquals(probability, binaryDictionary.getFrequency("aa")); + assertEquals(probability, binaryDictionary.getFrequency("aaaa")); + assertEquals(probability, binaryDictionary.getFrequency("a")); + assertEquals(updatedProbability, binaryDictionary.getFrequency("aaa")); + + dictFile.delete(); + } + + public void testRandomlyAddUnigramWord() { + final int wordCount = 1000; + final int codePointSetSize = 50; + final int seed = 123456789; + + File dictFile = null; + try { + dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary"); + } catch (IOException e) { + fail("IOException while writing an initial dictionary : " + e); + } + BinaryDictionary binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(), + 0 /* offset */, dictFile.length(), true /* useFullEditDistance */, + Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */); + + final HashMap<String, Integer> probabilityMap = new HashMap<String, Integer>(); + // Test a word that isn't contained within the dictionary. + final Random random = new Random(seed); + final int[] codePointSet = CodePointUtils.generateCodePointSet(codePointSetSize, random); + for (int i = 0; i < wordCount; ++i) { + final String word = CodePointUtils.generateWord(random, codePointSet); + probabilityMap.put(word, random.nextInt(0xFF)); + } + for (String word : probabilityMap.keySet()) { + binaryDictionary.addUnigramWord(word, probabilityMap.get(word)); + } + for (String word : probabilityMap.keySet()) { + assertEquals(word, (int)probabilityMap.get(word), binaryDictionary.getFrequency(word)); + } + dictFile.delete(); + } + + public void testAddBigramWords() { + File dictFile = null; + try { + dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary"); + } catch (IOException e) { + fail("IOException while writing an initial dictionary : " + e); + } + BinaryDictionary binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(), + 0 /* offset */, dictFile.length(), true /* useFullEditDistance */, + Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */); + + final int unigramProbability = 100; + final int bigramProbability = 10; + final int updatedBigramProbability = 15; + binaryDictionary.addUnigramWord("aaa", unigramProbability); + binaryDictionary.addUnigramWord("abb", unigramProbability); + binaryDictionary.addUnigramWord("bcc", unigramProbability); + binaryDictionary.addBigramWords("aaa", "abb", bigramProbability); + binaryDictionary.addBigramWords("aaa", "bcc", bigramProbability); + binaryDictionary.addBigramWords("abb", "aaa", bigramProbability); + binaryDictionary.addBigramWords("abb", "bcc", bigramProbability); + + final int probability = binaryDictionary.calculateProbability(unigramProbability, + bigramProbability); + assertEquals(true, binaryDictionary.isValidBigram("aaa", "abb")); + assertEquals(true, binaryDictionary.isValidBigram("aaa", "bcc")); + assertEquals(true, binaryDictionary.isValidBigram("abb", "aaa")); + assertEquals(true, binaryDictionary.isValidBigram("abb", "bcc")); + assertEquals(probability, binaryDictionary.getBigramProbability("aaa", "abb")); + assertEquals(probability, binaryDictionary.getBigramProbability("aaa", "bcc")); + assertEquals(probability, binaryDictionary.getBigramProbability("abb", "aaa")); + assertEquals(probability, binaryDictionary.getBigramProbability("abb", "bcc")); + + binaryDictionary.addBigramWords("aaa", "abb", updatedBigramProbability); + final int updatedProbability = binaryDictionary.calculateProbability(unigramProbability, + updatedBigramProbability); + assertEquals(updatedProbability, binaryDictionary.getBigramProbability("aaa", "abb")); + + assertEquals(false, binaryDictionary.isValidBigram("bcc", "aaa")); + assertEquals(false, binaryDictionary.isValidBigram("bcc", "bbc")); + assertEquals(false, binaryDictionary.isValidBigram("aaa", "aaa")); + assertEquals(Dictionary.NOT_A_PROBABILITY, + binaryDictionary.getBigramProbability("bcc", "aaa")); + assertEquals(Dictionary.NOT_A_PROBABILITY, + binaryDictionary.getBigramProbability("bcc", "bbc")); + assertEquals(Dictionary.NOT_A_PROBABILITY, + binaryDictionary.getBigramProbability("aaa", "aaa")); + + // Testing bigram link. + binaryDictionary.addUnigramWord("abcde", unigramProbability); + binaryDictionary.addUnigramWord("fghij", unigramProbability); + binaryDictionary.addBigramWords("abcde", "fghij", bigramProbability); + binaryDictionary.addUnigramWord("fgh", unigramProbability); + binaryDictionary.addUnigramWord("abc", unigramProbability); + binaryDictionary.addUnigramWord("f", unigramProbability); + assertEquals(probability, binaryDictionary.getBigramProbability("abcde", "fghij")); + assertEquals(Dictionary.NOT_A_PROBABILITY, + binaryDictionary.getBigramProbability("abcde", "fgh")); + binaryDictionary.addBigramWords("abcde", "fghij", updatedBigramProbability); + assertEquals(updatedProbability, binaryDictionary.getBigramProbability("abcde", "fghij")); + + dictFile.delete(); + } + + public void testRandomlyAddBigramWords() { + final int wordCount = 100; + final int bigramCount = 1000; + final int codePointSetSize = 50; + final int seed = 11111; + + File dictFile = null; + try { + dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary"); + } catch (IOException e) { + fail("IOException while writing an initial dictionary : " + e); + } + BinaryDictionary binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(), + 0 /* offset */, dictFile.length(), true /* useFullEditDistance */, + Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */); + final ArrayList<String> words = new ArrayList<String>(); + // Test a word that isn't contained within the dictionary. + final Random random = new Random(seed); + final int[] codePointSet = CodePointUtils.generateCodePointSet(codePointSetSize, random); + final int[] unigramProbabilities = new int[wordCount]; + for (int i = 0; i < wordCount; ++i) { + final String word = CodePointUtils.generateWord(random, codePointSet); + words.add(word); + final int unigramProbability = random.nextInt(0xFF); + unigramProbabilities[i] = unigramProbability; + binaryDictionary.addUnigramWord(word, unigramProbability); + } + + final int[][] probabilities = new int[wordCount][wordCount]; + + for (int i = 0; i < wordCount; ++i) { + for (int j = 0; j < wordCount; ++j) { + probabilities[i][j] = Dictionary.NOT_A_PROBABILITY; + } + } + + for (int i = 0; i < bigramCount; i++) { + final int word0Index = random.nextInt(wordCount); + final int word1Index = random.nextInt(wordCount); + final String word0 = words.get(word0Index); + final String word1 = words.get(word1Index); + final int bigramProbability = random.nextInt(0xF); + probabilities[word0Index][word1Index] = binaryDictionary.calculateProbability( + unigramProbabilities[word1Index], bigramProbability); + binaryDictionary.addBigramWords(word0, word1, bigramProbability); + } + + for (int i = 0; i < words.size(); i++) { + for (int j = 0; j < words.size(); j++) { + assertEquals(probabilities[i][j], + binaryDictionary.getBigramProbability(words.get(i), words.get(j))); + } + } + + dictFile.delete(); + } + + public void testRemoveBigramWords() { + File dictFile = null; + try { + dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary"); + } catch (IOException e) { + fail("IOException while writing an initial dictionary : " + e); + } + BinaryDictionary binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(), + 0 /* offset */, dictFile.length(), true /* useFullEditDistance */, + Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */); + final int unigramProbability = 100; + final int bigramProbability = 10; + binaryDictionary.addUnigramWord("aaa", unigramProbability); + binaryDictionary.addUnigramWord("abb", unigramProbability); + binaryDictionary.addUnigramWord("bcc", unigramProbability); + binaryDictionary.addBigramWords("aaa", "abb", bigramProbability); + binaryDictionary.addBigramWords("aaa", "bcc", bigramProbability); + binaryDictionary.addBigramWords("abb", "aaa", bigramProbability); + binaryDictionary.addBigramWords("abb", "bcc", bigramProbability); + + assertEquals(true, binaryDictionary.isValidBigram("aaa", "abb")); + assertEquals(true, binaryDictionary.isValidBigram("aaa", "bcc")); + assertEquals(true, binaryDictionary.isValidBigram("abb", "aaa")); + assertEquals(true, binaryDictionary.isValidBigram("abb", "bcc")); + + binaryDictionary.removeBigramWords("aaa", "abb"); + assertEquals(false, binaryDictionary.isValidBigram("aaa", "abb")); + binaryDictionary.addBigramWords("aaa", "abb", bigramProbability); + assertEquals(true, binaryDictionary.isValidBigram("aaa", "abb")); + + + binaryDictionary.removeBigramWords("aaa", "bcc"); + assertEquals(false, binaryDictionary.isValidBigram("aaa", "bcc")); + binaryDictionary.removeBigramWords("abb", "aaa"); + assertEquals(false, binaryDictionary.isValidBigram("abb", "aaa")); + binaryDictionary.removeBigramWords("abb", "bcc"); + assertEquals(false, binaryDictionary.isValidBigram("abb", "bcc")); + + binaryDictionary.removeBigramWords("aaa", "abb"); + // Test remove non-existing bigram operation. + binaryDictionary.removeBigramWords("aaa", "abb"); + binaryDictionary.removeBigramWords("bcc", "aaa"); + + dictFile.delete(); + } + + public void testFlushDictionary() { + File dictFile = null; + try { + dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary"); + } catch (IOException e) { + fail("IOException while writing an initial dictionary : " + e); + } + BinaryDictionary binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(), + 0 /* offset */, dictFile.length(), true /* useFullEditDistance */, + Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */); + + final int probability = 100; + binaryDictionary.addUnigramWord("aaa", probability); + binaryDictionary.addUnigramWord("abcd", probability); + // Close without flushing. + binaryDictionary.close(); + + binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(), + 0 /* offset */, dictFile.length(), true /* useFullEditDistance */, + Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */); + + assertEquals(Dictionary.NOT_A_PROBABILITY, binaryDictionary.getFrequency("aaa")); + assertEquals(Dictionary.NOT_A_PROBABILITY, binaryDictionary.getFrequency("abcd")); + + binaryDictionary.addUnigramWord("aaa", probability); + binaryDictionary.addUnigramWord("abcd", probability); + binaryDictionary.flush(); + binaryDictionary.close(); + + binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(), + 0 /* offset */, dictFile.length(), true /* useFullEditDistance */, + Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */); + + assertEquals(probability, binaryDictionary.getFrequency("aaa")); + assertEquals(probability, binaryDictionary.getFrequency("abcd")); + binaryDictionary.addUnigramWord("bcde", probability); + binaryDictionary.flush(); + binaryDictionary.close(); + + binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(), + 0 /* offset */, dictFile.length(), true /* useFullEditDistance */, + Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */); + assertEquals(probability, binaryDictionary.getFrequency("bcde")); + binaryDictionary.close(); + + dictFile.delete(); + } + + public void testFlushWithGCDictionary() { + File dictFile = null; + try { + dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary"); + } catch (IOException e) { + fail("IOException while writing an initial dictionary : " + e); + } + BinaryDictionary binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(), + 0 /* offset */, dictFile.length(), true /* useFullEditDistance */, + Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */); + + final int unigramProbability = 100; + final int bigramProbability = 10; + binaryDictionary.addUnigramWord("aaa", unigramProbability); + binaryDictionary.addUnigramWord("abb", unigramProbability); + binaryDictionary.addUnigramWord("bcc", unigramProbability); + binaryDictionary.addBigramWords("aaa", "abb", bigramProbability); + binaryDictionary.addBigramWords("aaa", "bcc", bigramProbability); + binaryDictionary.addBigramWords("abb", "aaa", bigramProbability); + binaryDictionary.addBigramWords("abb", "bcc", bigramProbability); + binaryDictionary.flushWithGC(); + binaryDictionary.close(); + + binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(), + 0 /* offset */, dictFile.length(), true /* useFullEditDistance */, + Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */); + final int probability = binaryDictionary.calculateProbability(unigramProbability, + bigramProbability); + assertEquals(unigramProbability, binaryDictionary.getFrequency("aaa")); + assertEquals(unigramProbability, binaryDictionary.getFrequency("abb")); + assertEquals(unigramProbability, binaryDictionary.getFrequency("bcc")); + assertEquals(probability, binaryDictionary.getBigramProbability("aaa", "abb")); + assertEquals(probability, binaryDictionary.getBigramProbability("aaa", "bcc")); + assertEquals(probability, binaryDictionary.getBigramProbability("abb", "aaa")); + assertEquals(probability, binaryDictionary.getBigramProbability("abb", "bcc")); + assertEquals(false, binaryDictionary.isValidBigram("bcc", "aaa")); + assertEquals(false, binaryDictionary.isValidBigram("bcc", "bbc")); + assertEquals(false, binaryDictionary.isValidBigram("aaa", "aaa")); + binaryDictionary.flushWithGC(); + binaryDictionary.close(); + + dictFile.delete(); + } + + // TODO: Evaluate performance of GC + public void testAddBigramWordsAndFlashWithGC() { + final int wordCount = 100; + final int bigramCount = 1000; + final int codePointSetSize = 30; + // TODO: Use various seeds such as a current timestamp to make this test more random. + final int seed = 314159265; + + File dictFile = null; + try { + dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary"); + } catch (IOException e) { + fail("IOException while writing an initial dictionary : " + e); + } + + BinaryDictionary binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(), + 0 /* offset */, dictFile.length(), true /* useFullEditDistance */, + Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */); + final ArrayList<String> words = new ArrayList<String>(); + // Test a word that isn't contained within the dictionary. + final Random random = new Random(seed); + final int[] codePointSet = CodePointUtils.generateCodePointSet(codePointSetSize, random); + final int[] unigramProbabilities = new int[wordCount]; + for (int i = 0; i < wordCount; ++i) { + final String word = CodePointUtils.generateWord(random, codePointSet); + words.add(word); + final int unigramProbability = random.nextInt(0xFF); + unigramProbabilities[i] = unigramProbability; + binaryDictionary.addUnigramWord(word, unigramProbability); + } + + final int[][] probabilities = new int[wordCount][wordCount]; + + for (int i = 0; i < wordCount; ++i) { + for (int j = 0; j < wordCount; ++j) { + probabilities[i][j] = Dictionary.NOT_A_PROBABILITY; + } + } + + for (int i = 0; i < bigramCount; i++) { + final int word0Index = random.nextInt(wordCount); + final int word1Index = random.nextInt(wordCount); + final String word0 = words.get(word0Index); + final String word1 = words.get(word1Index); + final int bigramProbability = random.nextInt(0xF); + probabilities[word0Index][word1Index] = binaryDictionary.calculateProbability( + unigramProbabilities[word1Index], bigramProbability); + binaryDictionary.addBigramWords(word0, word1, bigramProbability); + } + + binaryDictionary.flushWithGC(); + binaryDictionary.close(); + binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(), + 0 /* offset */, dictFile.length(), true /* useFullEditDistance */, + Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */); + + for (int i = 0; i < words.size(); i++) { + for (int j = 0; j < words.size(); j++) { + assertEquals(probabilities[i][j], + binaryDictionary.getBigramProbability(words.get(i), words.get(j))); + } + } + dictFile.delete(); + } + + public void testRandomOperetionsAndFlashWithGC() { + final int flashWithGCIterationCount = 50; + final int operationCountInEachIteration = 200; + final int initialUnigramCount = 100; + final float addUnigramProb = 0.5f; + final float addBigramProb = 0.8f; + final float removeBigramProb = 0.2f; + final int codePointSetSize = 30; + final int seed = 141421356; + + final Random random = new Random(seed); + + File dictFile = null; + try { + dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary"); + } catch (IOException e) { + fail("IOException while writing an initial dictionary : " + e); + } + + BinaryDictionary binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(), + 0 /* offset */, dictFile.length(), true /* useFullEditDistance */, + Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */); + final ArrayList<String> words = new ArrayList<String>(); + final ArrayList<Pair<String, String>> bigramWords = new ArrayList<Pair<String,String>>(); + final int[] codePointSet = CodePointUtils.generateCodePointSet(codePointSetSize, random); + final HashMap<String, Integer> unigramProbabilities = new HashMap<String, Integer>(); + final HashMap<Pair<String, String>, Integer> bigramProbabilities = + new HashMap<Pair<String, String>, Integer>(); + for (int i = 0; i < initialUnigramCount; ++i) { + final String word = CodePointUtils.generateWord(random, codePointSet); + words.add(word); + final int unigramProbability = random.nextInt(0xFF); + unigramProbabilities.put(word, unigramProbability); + binaryDictionary.addUnigramWord(word, unigramProbability); + } + binaryDictionary.flushWithGC(); + binaryDictionary.close(); + + for (int gcCount = 0; gcCount < flashWithGCIterationCount; gcCount++) { + binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(), + 0 /* offset */, dictFile.length(), true /* useFullEditDistance */, + Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */); + for (int opCount = 0; opCount < operationCountInEachIteration; opCount++) { + // Add unigram. + if (random.nextFloat() < addUnigramProb) { + final String word = CodePointUtils.generateWord(random, codePointSet); + words.add(word); + final int unigramProbability = random.nextInt(0xFF); + unigramProbabilities.put(word, unigramProbability); + binaryDictionary.addUnigramWord(word, unigramProbability); + } + // Add bigram. + if (random.nextFloat() < addBigramProb && words.size() > 2) { + final int word0Index = random.nextInt(words.size()); + int word1Index = random.nextInt(words.size() - 1); + if (word0Index <= word1Index) { + word1Index++; + } + final String word0 = words.get(word0Index); + final String word1 = words.get(word1Index); + final int bigramProbability = random.nextInt(0xF); + final Pair<String, String> bigram = new Pair<String, String>(word0, word1); + bigramWords.add(bigram); + bigramProbabilities.put(bigram, bigramProbability); + binaryDictionary.addBigramWords(word0, word1, bigramProbability); + } + // Remove bigram. + if (random.nextFloat() < removeBigramProb && !bigramWords.isEmpty()) { + final int bigramIndex = random.nextInt(bigramWords.size()); + final Pair<String, String> bigram = bigramWords.get(bigramIndex); + bigramWords.remove(bigramIndex); + bigramProbabilities.remove(bigram); + binaryDictionary.removeBigramWords(bigram.first, bigram.second); + } + } + + // Test whether the all unigram operations are collectlly handled. + for (int i = 0; i < words.size(); i++) { + final String word = words.get(i); + final int unigramProbability = unigramProbabilities.get(word); + assertEquals(word, unigramProbability, binaryDictionary.getFrequency(word)); + } + // Test whether the all bigram operations are collectlly handled. + for (int i = 0; i < bigramWords.size(); i++) { + final Pair<String, String> bigram = bigramWords.get(i); + final int unigramProbability = unigramProbabilities.get(bigram.second); + final int probability; + if (bigramProbabilities.containsKey(bigram)) { + final int bigramProbability = bigramProbabilities.get(bigram); + probability = binaryDictionary.calculateProbability(unigramProbability, + bigramProbability); + } else { + probability = Dictionary.NOT_A_PROBABILITY; + } + assertEquals(probability, + binaryDictionary.getBigramProbability(bigram.first, bigram.second)); + } + binaryDictionary.flushWithGC(); + binaryDictionary.close(); + } + + dictFile.delete(); + } + + public void testAddManyUnigramsAndFlushWithGC() { + final int flashWithGCIterationCount = 3; + final int codePointSetSize = 50; + final int seed = 22360679; + + final Random random = new Random(seed); + + File dictFile = null; + try { + dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary"); + } catch (IOException e) { + fail("IOException while writing an initial dictionary : " + e); + } + + final ArrayList<String> words = new ArrayList<String>(); + final HashMap<String, Integer> unigramProbabilities = new HashMap<String, Integer>(); + final int[] codePointSet = CodePointUtils.generateCodePointSet(codePointSetSize, random); + + BinaryDictionary binaryDictionary; + for (int i = 0; i < flashWithGCIterationCount; i++) { + binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(), + 0 /* offset */, dictFile.length(), true /* useFullEditDistance */, + Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */); + while(!binaryDictionary.needsToRunGC()) { + final String word = CodePointUtils.generateWord(random, codePointSet); + words.add(word); + final int unigramProbability = random.nextInt(0xFF); + unigramProbabilities.put(word, unigramProbability); + binaryDictionary.addUnigramWord(word, unigramProbability); + } + + for (int j = 0; j < words.size(); j++) { + final String word = words.get(j); + final int unigramProbability = unigramProbabilities.get(word); + assertEquals(word, unigramProbability, binaryDictionary.getFrequency(word)); + } + + binaryDictionary.flushWithGC(); + binaryDictionary.close(); + } + + dictFile.delete(); + } +} diff --git a/tests/src/com/android/inputmethod/latin/ExpandableDictionaryTests.java b/tests/src/com/android/inputmethod/latin/ExpandableDictionaryTests.java new file mode 100644 index 000000000..ecf3af736 --- /dev/null +++ b/tests/src/com/android/inputmethod/latin/ExpandableDictionaryTests.java @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2013 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.latin; + +import android.test.AndroidTestCase; +import android.test.suitebuilder.annotation.SmallTest; + +/** + * Unit test for ExpandableDictionary + */ +@SmallTest +public class ExpandableDictionaryTests extends AndroidTestCase { + + private final static int UNIGRAM_FREQ = 50; + + public void testAddWordAndGetWordFrequency() { + final ExpandableDictionary dict = new ExpandableDictionary(Dictionary.TYPE_USER); + + // Add words + dict.addWord("abcde", "abcde", UNIGRAM_FREQ); + dict.addWord("abcef", null, UNIGRAM_FREQ + 1); + + // Check words + assertFalse(dict.isValidWord("abcde")); + assertEquals(UNIGRAM_FREQ, dict.getWordFrequency("abcde")); + assertTrue(dict.isValidWord("abcef")); + assertEquals(UNIGRAM_FREQ+1, dict.getWordFrequency("abcef")); + + dict.addWord("abc", null, UNIGRAM_FREQ + 2); + assertTrue(dict.isValidWord("abc")); + assertEquals(UNIGRAM_FREQ + 2, dict.getWordFrequency("abc")); + + // Add existing word with lower frequency + dict.addWord("abc", null, UNIGRAM_FREQ); + assertEquals(UNIGRAM_FREQ + 2, dict.getWordFrequency("abc")); + + // Add existing word with higher frequency + dict.addWord("abc", null, UNIGRAM_FREQ + 3); + assertEquals(UNIGRAM_FREQ + 3, dict.getWordFrequency("abc")); + } +} diff --git a/tests/src/com/android/inputmethod/latin/FusionDictionaryTests.java b/tests/src/com/android/inputmethod/latin/FusionDictionaryTests.java index 65dfd2dde..cadd0f8f3 100644 --- a/tests/src/com/android/inputmethod/latin/FusionDictionaryTests.java +++ b/tests/src/com/android/inputmethod/latin/FusionDictionaryTests.java @@ -20,7 +20,7 @@ import android.test.AndroidTestCase; import android.test.suitebuilder.annotation.SmallTest; import com.android.inputmethod.latin.makedict.FusionDictionary; -import com.android.inputmethod.latin.makedict.FusionDictionary.Node; +import com.android.inputmethod.latin.makedict.FusionDictionary.PtNodeArray; import java.util.HashMap; @@ -30,21 +30,21 @@ import java.util.HashMap; @SmallTest public class FusionDictionaryTests extends AndroidTestCase { public void testFindWordInTree() { - FusionDictionary dict = new FusionDictionary(new Node(), + FusionDictionary dict = new FusionDictionary(new PtNodeArray(), new FusionDictionary.DictionaryOptions(new HashMap<String,String>(), false, false)); dict.add("abc", 10, null, false /* isNotAWord */); - assertNull(FusionDictionary.findWordInTree(dict.mRoot, "aaa")); - assertNotNull(FusionDictionary.findWordInTree(dict.mRoot, "abc")); + assertNull(FusionDictionary.findWordInTree(dict.mRootNodeArray, "aaa")); + assertNotNull(FusionDictionary.findWordInTree(dict.mRootNodeArray, "abc")); dict.add("aa", 10, null, false /* isNotAWord */); - assertNull(FusionDictionary.findWordInTree(dict.mRoot, "aaa")); - assertNotNull(FusionDictionary.findWordInTree(dict.mRoot, "aa")); + assertNull(FusionDictionary.findWordInTree(dict.mRootNodeArray, "aaa")); + assertNotNull(FusionDictionary.findWordInTree(dict.mRootNodeArray, "aa")); dict.add("babcd", 10, null, false /* isNotAWord */); dict.add("bacde", 10, null, false /* isNotAWord */); - assertNull(FusionDictionary.findWordInTree(dict.mRoot, "ba")); - assertNotNull(FusionDictionary.findWordInTree(dict.mRoot, "babcd")); - assertNotNull(FusionDictionary.findWordInTree(dict.mRoot, "bacde")); + assertNull(FusionDictionary.findWordInTree(dict.mRootNodeArray, "ba")); + assertNotNull(FusionDictionary.findWordInTree(dict.mRootNodeArray, "babcd")); + assertNotNull(FusionDictionary.findWordInTree(dict.mRootNodeArray, "bacde")); } } diff --git a/tests/src/com/android/inputmethod/latin/InputLogicTests.java b/tests/src/com/android/inputmethod/latin/InputLogicTests.java index 91401970a..cc2569f5e 100644 --- a/tests/src/com/android/inputmethod/latin/InputLogicTests.java +++ b/tests/src/com/android/inputmethod/latin/InputLogicTests.java @@ -17,6 +17,7 @@ package com.android.inputmethod.latin; import android.test.suitebuilder.annotation.LargeTest; +import android.view.inputmethod.BaseInputConnection; @LargeTest public class InputLogicTests extends InputTestsBase { @@ -133,6 +134,13 @@ public class InputLogicTests extends InputTestsBase { assertEquals("simple auto-correct", EXPECTED_RESULT, mEditText.getText().toString()); } + public void testAutoCorrectWithQuote() { + final String STRING_TO_TYPE = "didn' "; + final String EXPECTED_RESULT = "didn't "; + type(STRING_TO_TYPE); + assertEquals("auto-correct with quote", EXPECTED_RESULT, mEditText.getText().toString()); + } + public void testAutoCorrectWithPeriod() { final String STRING_TO_TYPE = "tgis."; final String EXPECTED_RESULT = "this."; @@ -179,7 +187,7 @@ public class InputLogicTests extends InputTestsBase { public void testCancelDoubleSpace() { final String STRING_TO_TYPE = "this "; - final String EXPECTED_RESULT = "this "; + final String EXPECTED_RESULT = "this "; type(STRING_TO_TYPE); type(Constants.CODE_DELETE); assertEquals("double space make a period", EXPECTED_RESULT, mEditText.getText().toString()); @@ -290,5 +298,47 @@ public class InputLogicTests extends InputTestsBase { } assertEquals("delete whole composing word", "", mEditText.getText().toString()); } + + public void testResumeSuggestionOnBackspace() { + final String WORD_TO_TYPE = "and this "; + type(WORD_TO_TYPE); + assertEquals("resume suggestion on backspace", -1, + BaseInputConnection.getComposingSpanStart(mEditText.getText())); + assertEquals("resume suggestion on backspace", -1, + BaseInputConnection.getComposingSpanEnd(mEditText.getText())); + type(Constants.CODE_DELETE); + assertEquals("resume suggestion on backspace", 4, + BaseInputConnection.getComposingSpanStart(mEditText.getText())); + assertEquals("resume suggestion on backspace", 8, + BaseInputConnection.getComposingSpanEnd(mEditText.getText())); + } + + private void helperTestComposing(final String wordToType, final boolean shouldBeComposing) { + mEditText.setText(""); + type(wordToType); + assertEquals("start composing inside text", shouldBeComposing ? 0 : -1, + BaseInputConnection.getComposingSpanStart(mEditText.getText())); + assertEquals("start composing inside text", shouldBeComposing ? wordToType.length() : -1, + BaseInputConnection.getComposingSpanEnd(mEditText.getText())); + } + + public void testStartComposing() { + // Should start composing on a letter + helperTestComposing("a", true); + type(" "); // To reset the composing state + // Should not start composing on quote + helperTestComposing("'", false); + type(" "); + helperTestComposing("'-", false); + type(" "); + // Should not start composing on dash + helperTestComposing("-", false); + type(" "); + helperTestComposing("-'", false); + type(" "); + helperTestComposing("a-", true); + type(" "); + helperTestComposing("a'", true); + } // TODO: Add some tests for non-BMP characters } diff --git a/tests/src/com/android/inputmethod/latin/InputLogicTestsLanguageWithoutSpaces.java b/tests/src/com/android/inputmethod/latin/InputLogicTestsLanguageWithoutSpaces.java new file mode 100644 index 000000000..0f0ebafb9 --- /dev/null +++ b/tests/src/com/android/inputmethod/latin/InputLogicTestsLanguageWithoutSpaces.java @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2013 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.latin; + +import android.test.suitebuilder.annotation.LargeTest; +import android.view.inputmethod.BaseInputConnection; + +import com.android.inputmethod.latin.suggestions.SuggestionStripView; + +@LargeTest +public class InputLogicTestsLanguageWithoutSpaces extends InputTestsBase { + public void testAutoCorrectForLanguageWithoutSpaces() { + final String STRING_TO_TYPE = "tgis is"; + final String EXPECTED_RESULT = "thisis"; + changeKeyboardLocaleAndDictLocale("th", "en_US"); + type(STRING_TO_TYPE); + assertEquals("simple auto-correct for language without spaces", EXPECTED_RESULT, + mEditText.getText().toString()); + } + + public void testRevertAutoCorrectForLanguageWithoutSpaces() { + final String STRING_TO_TYPE = "tgis "; + final String EXPECTED_INTERMEDIATE_RESULT = "this"; + final String EXPECTED_FINAL_RESULT = "tgis"; + changeKeyboardLocaleAndDictLocale("th", "en_US"); + type(STRING_TO_TYPE); + assertEquals("simple auto-correct for language without spaces", + EXPECTED_INTERMEDIATE_RESULT, mEditText.getText().toString()); + type(Constants.CODE_DELETE); + assertEquals("simple auto-correct for language without spaces", + EXPECTED_FINAL_RESULT, mEditText.getText().toString()); + // Check we are back to composing the word + assertEquals("don't resume suggestion on backspace", 0, + BaseInputConnection.getComposingSpanStart(mEditText.getText())); + assertEquals("don't resume suggestion on backspace", 4, + BaseInputConnection.getComposingSpanEnd(mEditText.getText())); + } + + public void testDontResumeSuggestionOnBackspace() { + final String WORD_TO_TYPE = "and this "; + changeKeyboardLocaleAndDictLocale("th", "en_US"); + type(WORD_TO_TYPE); + assertEquals("don't resume suggestion on backspace", -1, + BaseInputConnection.getComposingSpanStart(mEditText.getText())); + assertEquals("don't resume suggestion on backspace", -1, + BaseInputConnection.getComposingSpanEnd(mEditText.getText())); + type(" "); + type(Constants.CODE_DELETE); + assertEquals("don't resume suggestion on backspace", -1, + BaseInputConnection.getComposingSpanStart(mEditText.getText())); + assertEquals("don't resume suggestion on backspace", -1, + BaseInputConnection.getComposingSpanEnd(mEditText.getText())); + } + + public void testStartComposingInsideText() { + final String WORD_TO_TYPE = "abcdefgh "; + final int typedLength = WORD_TO_TYPE.length() - 1; // -1 because space gets eaten + final int CURSOR_POS = 4; + changeKeyboardLocaleAndDictLocale("th", "en_US"); + type(WORD_TO_TYPE); + mLatinIME.onUpdateSelection(0, 0, typedLength, typedLength, -1, -1); + mInputConnection.setSelection(CURSOR_POS, CURSOR_POS); + mLatinIME.onUpdateSelection(typedLength, typedLength, + CURSOR_POS, CURSOR_POS, -1, -1); + sleep(DELAY_TO_WAIT_FOR_PREDICTIONS); + runMessages(); + assertEquals("start composing inside text", -1, + BaseInputConnection.getComposingSpanStart(mEditText.getText())); + assertEquals("start composing inside text", -1, + BaseInputConnection.getComposingSpanEnd(mEditText.getText())); + type("xxxx"); + assertEquals("start composing inside text", 4, + BaseInputConnection.getComposingSpanStart(mEditText.getText())); + assertEquals("start composing inside text", 8, + BaseInputConnection.getComposingSpanEnd(mEditText.getText())); + } + + public void testPredictions() { + final String WORD_TO_TYPE = "Barack "; + changeKeyboardLocaleAndDictLocale("th", "en_US"); + type(WORD_TO_TYPE); + sleep(DELAY_TO_WAIT_FOR_PREDICTIONS); + runMessages(); + // Make sure there is no space + assertEquals("predictions in lang without spaces", "Barack", + mEditText.getText().toString()); + // Test the first prediction is displayed + assertEquals("predictions in lang without spaces", "Obama", + mLatinIME.getFirstSuggestedWord()); + } +} diff --git a/tests/src/com/android/inputmethod/latin/InputPointersTests.java b/tests/src/com/android/inputmethod/latin/InputPointersTests.java index 301582979..5095f9606 100644 --- a/tests/src/com/android/inputmethod/latin/InputPointersTests.java +++ b/tests/src/com/android/inputmethod/latin/InputPointersTests.java @@ -19,6 +19,8 @@ package com.android.inputmethod.latin; import android.test.AndroidTestCase; import android.test.suitebuilder.annotation.SmallTest; +import com.android.inputmethod.latin.utils.ResizableIntArray; + import java.util.Arrays; @SmallTest @@ -242,4 +244,20 @@ public class InputPointersTests extends AndroidTestCase { expecteds[i + expectedPos], actuals[i + actualPos]); } } + + public void testShift() { + final InputPointers src = new InputPointers(DEFAULT_CAPACITY); + final int limit = 100; + final int shiftAmount = 20; + for (int i = 0; i < limit; i++) { + src.addPointer(i, i * 2, i * 3, i * 4); + } + src.shift(shiftAmount); + for (int i = 0; i < limit - shiftAmount; ++i) { + assertEquals("xCoordinates at " + i, i + shiftAmount, src.getXCoordinates()[i]); + assertEquals("yCoordinates at " + i, (i + shiftAmount) * 2, src.getYCoordinates()[i]); + assertEquals("pointerIds at " + i, (i + shiftAmount) * 3, src.getPointerIds()[i]); + assertEquals("times at " + i, (i + shiftAmount) * 4, src.getTimes()[i]); + } + } } diff --git a/tests/src/com/android/inputmethod/latin/InputTestsBase.java b/tests/src/com/android/inputmethod/latin/InputTestsBase.java index aec4aaca1..234bb1b31 100644 --- a/tests/src/com/android/inputmethod/latin/InputTestsBase.java +++ b/tests/src/com/android/inputmethod/latin/InputTestsBase.java @@ -36,6 +36,7 @@ import android.widget.FrameLayout; import com.android.inputmethod.keyboard.Key; import com.android.inputmethod.keyboard.Keyboard; import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; +import com.android.inputmethod.latin.utils.LocaleUtils; import java.util.Locale; @@ -43,8 +44,10 @@ public class InputTestsBase extends ServiceTestCase<LatinIMEForTests> { private static final String PREF_DEBUG_MODE = "debug_mode"; - // The message that sets the underline is posted with a 100 ms delay + // The message that sets the underline is posted with a 200 ms delay protected static final int DELAY_TO_WAIT_FOR_UNDERLINE = 200; + // The message that sets predictions is posted with a 200 ms delay + protected static final int DELAY_TO_WAIT_FOR_PREDICTIONS = 200; protected LatinIME mLatinIME; protected Keyboard mKeyboard; @@ -203,17 +206,16 @@ public class InputTestsBase extends ServiceTestCase<LatinIMEForTests> { // view and only delegates to the parts of the code that care. So we don't include them here // to keep these tests as pinpoint as possible and avoid bringing it too many dependencies, // but keep them in mind if something breaks. Commenting them out as is should work. - //mLatinIME.onPressKey(codePoint); - for (final Key key : mKeyboard.mKeys) { - if (key.mCode == codePoint) { - final int x = key.mX + key.mWidth / 2; - final int y = key.mY + key.mHeight / 2; - mLatinIME.onCodeInput(codePoint, x, y); - return; - } + //mLatinIME.onPressKey(codePoint, 0 /* repeatCount */, true /* isSinglePointer */); + final Key key = mKeyboard.getKey(codePoint); + if (key != null) { + final int x = key.getX() + key.getWidth() / 2; + final int y = key.getY() + key.getHeight() / 2; + mLatinIME.onCodeInput(codePoint, x, y); + return; } mLatinIME.onCodeInput(codePoint, Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE); - //mLatinIME.onReleaseKey(codePoint, false); + //mLatinIME.onReleaseKey(codePoint, false /* withSliding */); } protected void type(final String stringToType) { @@ -224,7 +226,7 @@ public class InputTestsBase extends ServiceTestCase<LatinIMEForTests> { protected void waitForDictionaryToBeLoaded() { int remainingAttempts = 300; - while (remainingAttempts > 0 && mLatinIME.mSuggest.isCurrentlyWaitingForMainDictionary()) { + while (remainingAttempts > 0 && mLatinIME.isCurrentlyWaitingForMainDictionary()) { try { Thread.sleep(200); } catch (InterruptedException e) { @@ -233,22 +235,32 @@ public class InputTestsBase extends ServiceTestCase<LatinIMEForTests> { --remainingAttempts; } } - if (!mLatinIME.mSuggest.hasMainDictionary()) { - throw new RuntimeException("Can't initialize the main dictionary"); - } } protected void changeLanguage(final String locale) { mEditText.mCurrentLocale = LocaleUtils.constructLocaleFromString(locale); SubtypeSwitcher.getInstance().forceLocale(mEditText.mCurrentLocale); mLatinIME.loadKeyboard(); + runMessages(); mKeyboard = mLatinIME.mKeyboardSwitcher.getKeyboard(); waitForDictionaryToBeLoaded(); } + protected void changeKeyboardLocaleAndDictLocale(final String keyboardLocale, + final String dictLocale) { + changeLanguage(keyboardLocale); + if (!keyboardLocale.equals(dictLocale)) { + mLatinIME.replaceMainDictionaryForTest( + LocaleUtils.constructLocaleFromString(dictLocale)); + } + waitForDictionaryToBeLoaded(); + } + protected void pickSuggestionManually(final int index, final String suggestion) { mLatinIME.pickSuggestionManually(index, new SuggestedWordInfo(suggestion, 1, - SuggestedWordInfo.KIND_CORRECTION, "main")); + SuggestedWordInfo.KIND_CORRECTION, null /* sourceDict */, + SuggestedWordInfo.NOT_AN_INDEX /* indexOfTouchPointOfSecondWord */, + SuggestedWordInfo.NOT_A_CONFIDENCE /* autoCommitFirstWordConfidence */)); } // Helper to avoid writing the try{}catch block each time diff --git a/tests/src/com/android/inputmethod/latin/RichInputConnectionTests.java b/tests/src/com/android/inputmethod/latin/RichInputConnectionAndTextRangeTests.java index aacd60f4d..c0dd9933c 100644 --- a/tests/src/com/android/inputmethod/latin/RichInputConnectionTests.java +++ b/tests/src/com/android/inputmethod/latin/RichInputConnectionAndTextRangeTests.java @@ -16,19 +16,26 @@ package com.android.inputmethod.latin; +import com.android.inputmethod.latin.utils.TextRange; + import android.inputmethodservice.InputMethodService; +import android.os.Parcel; import android.test.AndroidTestCase; +import android.test.MoreAsserts; import android.test.suitebuilder.annotation.SmallTest; +import android.text.SpannableString; +import android.text.Spanned; import android.text.TextUtils; +import android.text.style.SuggestionSpan; import android.view.inputmethod.ExtractedText; import android.view.inputmethod.ExtractedTextRequest; import android.view.inputmethod.InputConnection; import android.view.inputmethod.InputConnectionWrapper; -import com.android.inputmethod.latin.RichInputConnection.Range; +import java.util.Locale; @SmallTest -public class RichInputConnectionTests extends AndroidTestCase { +public class RichInputConnectionAndTextRangeTests extends AndroidTestCase { // The following is meant to be a reasonable default for // the "word_separators" resource. @@ -40,10 +47,30 @@ public class RichInputConnectionTests extends AndroidTestCase { } private class MockConnection extends InputConnectionWrapper { - final String mTextBefore; - final String mTextAfter; + final CharSequence mTextBefore; + final CharSequence mTextAfter; final ExtractedText mExtractedText; + public MockConnection(final CharSequence text, final int cursorPosition) { + super(null, false); + // Interaction of spans with Parcels is completely non-trivial, but in the actual case + // the CharSequences do go through Parcels because they go through IPC. There + // are some significant differences between the behavior of Spanned objects that + // have and that have not gone through parceling, so it's much easier to simulate + // the environment with Parcels than try to emulate things by hand. + final Parcel p = Parcel.obtain(); + TextUtils.writeToParcel(text.subSequence(0, cursorPosition), p, 0 /* flags */); + TextUtils.writeToParcel(text.subSequence(cursorPosition, text.length()), p, + 0 /* flags */); + final byte[] marshalled = p.marshall(); + p.unmarshall(marshalled, 0, marshalled.length); + p.setDataPosition(0); + mTextBefore = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(p); + mTextAfter = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(p); + mExtractedText = null; + p.recycle(); + } + public MockConnection(String textBefore, String textAfter, ExtractedText extractedText) { super(null, false); mTextBefore = textBefore; @@ -142,7 +169,7 @@ public class RichInputConnectionTests extends AndroidTestCase { mockInputMethodService.setInputConnection(new MockConnection("word wo", "rd", et)); et.startOffset = 0; et.selectionStart = 7; - Range r; + TextRange r; ic.beginBatchEdit(); // basic case @@ -191,4 +218,95 @@ public class RichInputConnectionTests extends AndroidTestCase { ic.endBatchEdit(); assertTrue(TextUtils.equals("word", r.mWord)); } + + /** + * Test logic in getting the word range at the cursor. + */ + public void testGetSuggestionSpansAtWord() { + helpTestGetSuggestionSpansAtWord(10); + helpTestGetSuggestionSpansAtWord(12); + helpTestGetSuggestionSpansAtWord(15); + helpTestGetSuggestionSpansAtWord(16); + } + + private void helpTestGetSuggestionSpansAtWord(final int cursorPos) { + final MockInputMethodService mockInputMethodService = new MockInputMethodService(); + final RichInputConnection ic = new RichInputConnection(mockInputMethodService); + + final String[] SUGGESTIONS1 = { "swing", "strong" }; + final String[] SUGGESTIONS2 = { "storing", "strung" }; + + // Test the usual case. + SpannableString text = new SpannableString("This is a string for test"); + text.setSpan(new SuggestionSpan(Locale.ENGLISH, SUGGESTIONS1, 0 /* flags */), + 10 /* start */, 16 /* end */, 0 /* flags */); + mockInputMethodService.setInputConnection(new MockConnection(text, cursorPos)); + TextRange r; + SuggestionSpan[] suggestions; + + r = ic.getWordRangeAtCursor(" ", 0); + suggestions = r.getSuggestionSpansAtWord(); + assertEquals(suggestions.length, 1); + MoreAsserts.assertEquals(suggestions[0].getSuggestions(), SUGGESTIONS1); + + // Test the case with 2 suggestion spans in the same place. + text = new SpannableString("This is a string for test"); + text.setSpan(new SuggestionSpan(Locale.ENGLISH, SUGGESTIONS1, 0 /* flags */), + 10 /* start */, 16 /* end */, 0 /* flags */); + text.setSpan(new SuggestionSpan(Locale.ENGLISH, SUGGESTIONS2, 0 /* flags */), + 10 /* start */, 16 /* end */, 0 /* flags */); + mockInputMethodService.setInputConnection(new MockConnection(text, cursorPos)); + r = ic.getWordRangeAtCursor(" ", 0); + suggestions = r.getSuggestionSpansAtWord(); + assertEquals(suggestions.length, 2); + MoreAsserts.assertEquals(suggestions[0].getSuggestions(), SUGGESTIONS1); + MoreAsserts.assertEquals(suggestions[1].getSuggestions(), SUGGESTIONS2); + + // Test a case with overlapping spans, 2nd extending past the start of the word + text = new SpannableString("This is a string for test"); + text.setSpan(new SuggestionSpan(Locale.ENGLISH, SUGGESTIONS1, 0 /* flags */), + 10 /* start */, 16 /* end */, 0 /* flags */); + text.setSpan(new SuggestionSpan(Locale.ENGLISH, SUGGESTIONS2, 0 /* flags */), + 5 /* start */, 16 /* end */, 0 /* flags */); + mockInputMethodService.setInputConnection(new MockConnection(text, cursorPos)); + r = ic.getWordRangeAtCursor(" ", 0); + suggestions = r.getSuggestionSpansAtWord(); + assertEquals(suggestions.length, 1); + MoreAsserts.assertEquals(suggestions[0].getSuggestions(), SUGGESTIONS1); + + // Test a case with overlapping spans, 2nd extending past the end of the word + text = new SpannableString("This is a string for test"); + text.setSpan(new SuggestionSpan(Locale.ENGLISH, SUGGESTIONS1, 0 /* flags */), + 10 /* start */, 16 /* end */, 0 /* flags */); + text.setSpan(new SuggestionSpan(Locale.ENGLISH, SUGGESTIONS2, 0 /* flags */), + 10 /* start */, 20 /* end */, 0 /* flags */); + mockInputMethodService.setInputConnection(new MockConnection(text, cursorPos)); + r = ic.getWordRangeAtCursor(" ", 0); + suggestions = r.getSuggestionSpansAtWord(); + assertEquals(suggestions.length, 1); + MoreAsserts.assertEquals(suggestions[0].getSuggestions(), SUGGESTIONS1); + + // Test a case with overlapping spans, 2nd extending past both ends of the word + text = new SpannableString("This is a string for test"); + text.setSpan(new SuggestionSpan(Locale.ENGLISH, SUGGESTIONS1, 0 /* flags */), + 10 /* start */, 16 /* end */, 0 /* flags */); + text.setSpan(new SuggestionSpan(Locale.ENGLISH, SUGGESTIONS2, 0 /* flags */), + 5 /* start */, 20 /* end */, 0 /* flags */); + mockInputMethodService.setInputConnection(new MockConnection(text, cursorPos)); + r = ic.getWordRangeAtCursor(" ", 0); + suggestions = r.getSuggestionSpansAtWord(); + assertEquals(suggestions.length, 1); + MoreAsserts.assertEquals(suggestions[0].getSuggestions(), SUGGESTIONS1); + + // Test a case with overlapping spans, none right on the word + text = new SpannableString("This is a string for test"); + text.setSpan(new SuggestionSpan(Locale.ENGLISH, SUGGESTIONS1, 0 /* flags */), + 5 /* start */, 16 /* end */, 0 /* flags */); + text.setSpan(new SuggestionSpan(Locale.ENGLISH, SUGGESTIONS2, 0 /* flags */), + 5 /* start */, 20 /* end */, 0 /* flags */); + mockInputMethodService.setInputConnection(new MockConnection(text, cursorPos)); + r = ic.getWordRangeAtCursor(" ", 0); + suggestions = r.getSuggestionSpansAtWord(); + assertEquals(suggestions.length, 0); + } } diff --git a/tests/src/com/android/inputmethod/latin/SubtypeLocaleTests.java b/tests/src/com/android/inputmethod/latin/SubtypeLocaleTests.java deleted file mode 100644 index abfaf3062..000000000 --- a/tests/src/com/android/inputmethod/latin/SubtypeLocaleTests.java +++ /dev/null @@ -1,357 +0,0 @@ -/* - * Copyright (C) 2011 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.latin; - -import android.content.Context; -import android.content.res.Resources; -import android.test.AndroidTestCase; -import android.test.suitebuilder.annotation.SmallTest; -import android.view.inputmethod.InputMethodSubtype; - -import com.android.inputmethod.latin.LocaleUtils.RunInLocale; - -import java.util.ArrayList; -import java.util.Locale; - -@SmallTest -public class SubtypeLocaleTests extends AndroidTestCase { - // Locale to subtypes list. - private final ArrayList<InputMethodSubtype> mSubtypesList = CollectionUtils.newArrayList(); - - private RichInputMethodManager mRichImm; - private Resources mRes; - - InputMethodSubtype EN_US; - InputMethodSubtype EN_GB; - InputMethodSubtype ES_US; - InputMethodSubtype FR; - InputMethodSubtype FR_CA; - InputMethodSubtype DE; - InputMethodSubtype ZZ; - InputMethodSubtype DE_QWERTY; - InputMethodSubtype FR_QWERTZ; - InputMethodSubtype EN_US_AZERTY; - InputMethodSubtype EN_UK_DVORAK; - InputMethodSubtype ES_US_COLEMAK; - InputMethodSubtype ZZ_PC; - - @Override - protected void setUp() throws Exception { - super.setUp(); - final Context context = getContext(); - RichInputMethodManager.init(context); - mRichImm = RichInputMethodManager.getInstance(); - mRes = context.getResources(); - SubtypeLocale.init(context); - - EN_US = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet( - Locale.US.toString(), "qwerty"); - EN_GB = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet( - Locale.UK.toString(), "qwerty"); - ES_US = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet( - "es_US", "spanish"); - FR = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet( - Locale.FRENCH.toString(), "azerty"); - FR_CA = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet( - Locale.CANADA_FRENCH.toString(), "qwerty"); - DE = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet( - Locale.GERMAN.toString(), "qwertz"); - ZZ = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet( - SubtypeLocale.NO_LANGUAGE, "qwerty"); - DE_QWERTY = AdditionalSubtype.createAdditionalSubtype( - Locale.GERMAN.toString(), "qwerty", null); - FR_QWERTZ = AdditionalSubtype.createAdditionalSubtype( - Locale.FRENCH.toString(), "qwertz", null); - EN_US_AZERTY = AdditionalSubtype.createAdditionalSubtype( - Locale.US.toString(), "azerty", null); - EN_UK_DVORAK = AdditionalSubtype.createAdditionalSubtype( - Locale.UK.toString(), "dvorak", null); - ES_US_COLEMAK = AdditionalSubtype.createAdditionalSubtype( - "es_US", "colemak", null); - ZZ_PC = AdditionalSubtype.createAdditionalSubtype( - SubtypeLocale.NO_LANGUAGE, "pcqwerty", null); - - } - - public void testAllFullDisplayName() { - for (final InputMethodSubtype subtype : mSubtypesList) { - final String subtypeName = SubtypeLocale.getSubtypeDisplayName(subtype); - if (SubtypeLocale.isNoLanguage(subtype)) { - final String noLanguage = mRes.getString(R.string.subtype_no_language); - assertTrue(subtypeName, subtypeName.contains(noLanguage)); - } else { - final String languageName = - SubtypeLocale.getSubtypeLocaleDisplayName(subtype.getLocale()); - assertTrue(subtypeName, subtypeName.contains(languageName)); - } - } - } - - // InputMethodSubtype's display name in its locale. - // isAdditionalSubtype (T=true, F=false) - // locale layout | display name - // ------ ------- - ---------------------- - // en_US qwerty F English (US) exception - // en_GB qwerty F English (UK) exception - // es_US spanish F Español (EE.UU.) exception - // fr azerty F Français - // fr_CA qwerty F Français (Canada) - // de qwertz F Deutsch - // zz qwerty F No language (QWERTY) in system locale - // fr qwertz T Français (QWERTZ) - // de qwerty T Deutsch (QWERTY) - // en_US azerty T English (US) (AZERTY) exception - // en_UK dvorak T English (UK) (Dvorak) exception - // es_US colemak T Español (EE.UU.) (Colemak) exception - // zz pc T No language (PC) in system locale - - public void testPredefinedSubtypesInEnglish() { - assertEquals("en_US", "qwerty", SubtypeLocale.getKeyboardLayoutSetName(EN_US)); - assertEquals("en_GB", "qwerty", SubtypeLocale.getKeyboardLayoutSetName(EN_GB)); - assertEquals("es_US", "spanish", SubtypeLocale.getKeyboardLayoutSetName(ES_US)); - assertEquals("fr ", "azerty", SubtypeLocale.getKeyboardLayoutSetName(FR)); - assertEquals("fr_CA", "qwerty", SubtypeLocale.getKeyboardLayoutSetName(FR_CA)); - assertEquals("de ", "qwertz", SubtypeLocale.getKeyboardLayoutSetName(DE)); - assertEquals("zz ", "qwerty", SubtypeLocale.getKeyboardLayoutSetName(ZZ)); - - final RunInLocale<Void> tests = new RunInLocale<Void>() { - @Override - protected Void job(Resources res) { - assertEquals("en_US", "English (US)", - SubtypeLocale.getSubtypeDisplayName(EN_US)); - assertEquals("en_GB", "English (UK)", - SubtypeLocale.getSubtypeDisplayName(EN_GB)); - assertEquals("es_US", "Español (EE.UU.)", - SubtypeLocale.getSubtypeDisplayName(ES_US)); - assertEquals("fr ", "Français", - SubtypeLocale.getSubtypeDisplayName(FR)); - assertEquals("fr_CA", "Français (Canada)", - SubtypeLocale.getSubtypeDisplayName(FR_CA)); - assertEquals("de ", "Deutsch", - SubtypeLocale.getSubtypeDisplayName(DE)); - assertEquals("zz ", "No language (QWERTY)", - SubtypeLocale.getSubtypeDisplayName(ZZ)); - return null; - } - }; - tests.runInLocale(mRes, Locale.ENGLISH); - } - - public void testAdditionalSubtypesInEnglish() { - final RunInLocale<Void> tests = new RunInLocale<Void>() { - @Override - protected Void job(Resources res) { - assertEquals("fr qwertz", "Français (QWERTZ)", - SubtypeLocale.getSubtypeDisplayName(FR_QWERTZ)); - assertEquals("de qwerty", "Deutsch (QWERTY)", - SubtypeLocale.getSubtypeDisplayName(DE_QWERTY)); - assertEquals("en_US azerty", "English (US) (AZERTY)", - SubtypeLocale.getSubtypeDisplayName(EN_US_AZERTY)); - assertEquals("en_UK dvorak", "English (UK) (Dvorak)", - SubtypeLocale.getSubtypeDisplayName(EN_UK_DVORAK)); - assertEquals("es_US colemak","Español (EE.UU.) (Colemak)", - SubtypeLocale.getSubtypeDisplayName(ES_US_COLEMAK)); - assertEquals("zz pc", "No language (PC)", - SubtypeLocale.getSubtypeDisplayName(ZZ_PC)); - return null; - } - }; - tests.runInLocale(mRes, Locale.ENGLISH); - } - - public void testPredefinedSubtypesInFrench() { - final RunInLocale<Void> tests = new RunInLocale<Void>() { - @Override - protected Void job(Resources res) { - assertEquals("en_US", "English (US)", - SubtypeLocale.getSubtypeDisplayName(EN_US)); - assertEquals("en_GB", "English (UK)", - SubtypeLocale.getSubtypeDisplayName(EN_GB)); - assertEquals("es_US", "Español (EE.UU.)", - SubtypeLocale.getSubtypeDisplayName(ES_US)); - assertEquals("fr ", "Français", - SubtypeLocale.getSubtypeDisplayName(FR)); - assertEquals("fr_CA", "Français (Canada)", - SubtypeLocale.getSubtypeDisplayName(FR_CA)); - assertEquals("de ", "Deutsch", - SubtypeLocale.getSubtypeDisplayName(DE)); - assertEquals("zz ", "Aucune langue (QWERTY)", - SubtypeLocale.getSubtypeDisplayName(ZZ)); - return null; - } - }; - tests.runInLocale(mRes, Locale.FRENCH); - } - - public void testAdditionalSubtypesInFrench() { - final RunInLocale<Void> tests = new RunInLocale<Void>() { - @Override - protected Void job(Resources res) { - assertEquals("fr qwertz", "Français (QWERTZ)", - SubtypeLocale.getSubtypeDisplayName(FR_QWERTZ)); - assertEquals("de qwerty", "Deutsch (QWERTY)", - SubtypeLocale.getSubtypeDisplayName(DE_QWERTY)); - assertEquals("en_US azerty", "English (US) (AZERTY)", - SubtypeLocale.getSubtypeDisplayName(EN_US_AZERTY)); - assertEquals("en_UK dvorak", "English (UK) (Dvorak)", - SubtypeLocale.getSubtypeDisplayName(EN_UK_DVORAK)); - assertEquals("es_US colemak","Español (EE.UU.) (Colemak)", - SubtypeLocale.getSubtypeDisplayName(ES_US_COLEMAK)); - assertEquals("zz azerty", "Aucune langue (PC)", - SubtypeLocale.getSubtypeDisplayName(ZZ_PC)); - return null; - } - }; - tests.runInLocale(mRes, Locale.FRENCH); - } - - // InputMethodSubtype's display name in system locale (en_US). - // isAdditionalSubtype (T=true, F=false) - // locale layout | display name - // ------ ------- - ---------------------- - // en_US qwerty F English (US) exception - // en_GB qwerty F English (UK) exception - // es_US spanish F Spanish (US) exception - // fr azerty F French - // fr_CA qwerty F French (Canada) - // de qwertz F German - // zz qwerty F No language (QWERTY) - // fr qwertz T French (QWERTZ) - // de qwerty T German (QWERTY) - // en_US azerty T English (US) (AZERTY) exception - // en_UK dvorak T English (UK) (Dvorak) exception - // es_US colemak T Spanish (US) (Colemak) exception - // zz pc T No language (PC) - - public void testPredefinedSubtypesInEnglishSystemLocale() { - assertEquals("en_US", "qwerty", SubtypeLocale.getKeyboardLayoutSetName(EN_US)); - assertEquals("en_GB", "qwerty", SubtypeLocale.getKeyboardLayoutSetName(EN_GB)); - assertEquals("es_US", "spanish", SubtypeLocale.getKeyboardLayoutSetName(ES_US)); - assertEquals("fr ", "azerty", SubtypeLocale.getKeyboardLayoutSetName(FR)); - assertEquals("fr_CA", "qwerty", SubtypeLocale.getKeyboardLayoutSetName(FR_CA)); - assertEquals("de ", "qwertz", SubtypeLocale.getKeyboardLayoutSetName(DE)); - assertEquals("zz ", "qwerty", SubtypeLocale.getKeyboardLayoutSetName(ZZ)); - - final RunInLocale<Void> tests = new RunInLocale<Void>() { - @Override - protected Void job(Resources res) { - assertEquals("en_US", "English (US)", - SubtypeLocale.getSubtypeDisplayNameInSystemLocale(EN_US)); - assertEquals("en_GB", "English (UK)", - SubtypeLocale.getSubtypeDisplayNameInSystemLocale(EN_GB)); - assertEquals("es_US", "Spanish (US)", - SubtypeLocale.getSubtypeDisplayNameInSystemLocale(ES_US)); - assertEquals("fr ", "French", - SubtypeLocale.getSubtypeDisplayNameInSystemLocale(FR)); - assertEquals("fr_CA", "French (Canada)", - SubtypeLocale.getSubtypeDisplayNameInSystemLocale(FR_CA)); - assertEquals("de ", "German", - SubtypeLocale.getSubtypeDisplayNameInSystemLocale(DE)); - assertEquals("zz ", "No language (QWERTY)", - SubtypeLocale.getSubtypeDisplayNameInSystemLocale(ZZ)); - return null; - } - }; - tests.runInLocale(mRes, Locale.ENGLISH); - } - - public void testAdditionalSubtypesInEnglishSystemLocale() { - final RunInLocale<Void> tests = new RunInLocale<Void>() { - @Override - protected Void job(Resources res) { - assertEquals("fr qwertz", "French (QWERTZ)", - SubtypeLocale.getSubtypeDisplayNameInSystemLocale(FR_QWERTZ)); - assertEquals("de qwerty", "German (QWERTY)", - SubtypeLocale.getSubtypeDisplayNameInSystemLocale(DE_QWERTY)); - assertEquals("en_US azerty", "English (US) (AZERTY)", - SubtypeLocale.getSubtypeDisplayNameInSystemLocale(EN_US_AZERTY)); - assertEquals("en_UK dvorak", "English (UK) (Dvorak)", - SubtypeLocale.getSubtypeDisplayNameInSystemLocale(EN_UK_DVORAK)); - assertEquals("es_US colemak","Spanish (US) (Colemak)", - SubtypeLocale.getSubtypeDisplayNameInSystemLocale(ES_US_COLEMAK)); - assertEquals("zz azerty", "No language (PC)", - SubtypeLocale.getSubtypeDisplayNameInSystemLocale(ZZ_PC)); - return null; - } - }; - tests.runInLocale(mRes, Locale.ENGLISH); - } - - // InputMethodSubtype's display name in system locale (fr). - // isAdditionalSubtype (T=true, F=false) - // locale layout | display name - // ------ ------- - ---------------------- - // en_US qwerty F Anglais (États-Unis) exception - // en_GB qwerty F Anglais (Royaume-Uni) exception - // es_US spanish F Espagnol (États-Unis) exception - // fr azerty F Français - // fr_CA qwerty F Français (Canada) - // de qwertz F Allemand - // zz qwerty F Aucune langue (QWERTY) - // fr qwertz T Français (QWERTZ) - // de qwerty T Allemand (QWERTY) - // en_US azerty T Anglais (États-Unis) (AZERTY) exception - // en_UK dvorak T Anglais (Royaume-Uni) (Dvorak) exception - // es_US colemak T Espagnol (États-Unis) (Colemak) exception - // zz pc T Aucune langue (PC) - - public void testPredefinedSubtypesInFrenchSystemLocale() { - final RunInLocale<Void> tests = new RunInLocale<Void>() { - @Override - protected Void job(Resources res) { - assertEquals("en_US", "Anglais (États-Unis)", - SubtypeLocale.getSubtypeDisplayNameInSystemLocale(EN_US)); - assertEquals("en_GB", "Anglais (Royaume-Uni)", - SubtypeLocale.getSubtypeDisplayNameInSystemLocale(EN_GB)); - assertEquals("es_US", "Espagnol (États-Unis)", - SubtypeLocale.getSubtypeDisplayNameInSystemLocale(ES_US)); - assertEquals("fr ", "Français", - SubtypeLocale.getSubtypeDisplayNameInSystemLocale(FR)); - assertEquals("fr_CA", "Français (Canada)", - SubtypeLocale.getSubtypeDisplayNameInSystemLocale(FR_CA)); - assertEquals("de ", "Allemand", - SubtypeLocale.getSubtypeDisplayNameInSystemLocale(DE)); - assertEquals("zz ", "Aucune langue (QWERTY)", - SubtypeLocale.getSubtypeDisplayNameInSystemLocale(ZZ)); - return null; - } - }; - tests.runInLocale(mRes, Locale.FRENCH); - } - - public void testAdditionalSubtypesInFrenchSystemLocale() { - final RunInLocale<Void> tests = new RunInLocale<Void>() { - @Override - protected Void job(Resources res) { - assertEquals("fr qwertz", "Français (QWERTZ)", - SubtypeLocale.getSubtypeDisplayNameInSystemLocale(FR_QWERTZ)); - assertEquals("de qwerty", "Allemand (QWERTY)", - SubtypeLocale.getSubtypeDisplayNameInSystemLocale(DE_QWERTY)); - assertEquals("en_US azerty", "Anglais (États-Unis) (AZERTY)", - SubtypeLocale.getSubtypeDisplayNameInSystemLocale(EN_US_AZERTY)); - assertEquals("en_UK dvorak", "Anglais (Royaume-Uni) (Dvorak)", - SubtypeLocale.getSubtypeDisplayNameInSystemLocale(EN_UK_DVORAK)); - assertEquals("es_US colemak","Espagnol (États-Unis) (Colemak)", - SubtypeLocale.getSubtypeDisplayNameInSystemLocale(ES_US_COLEMAK)); - assertEquals("zz azerty", "Aucune langue (PC)", - SubtypeLocale.getSubtypeDisplayNameInSystemLocale(ZZ_PC)); - return null; - } - }; - tests.runInLocale(mRes, Locale.FRENCH); - } -} diff --git a/tests/src/com/android/inputmethod/latin/SuggestedWordsTests.java b/tests/src/com/android/inputmethod/latin/SuggestedWordsTests.java index 916252292..a594baf0b 100644 --- a/tests/src/com/android/inputmethod/latin/SuggestedWordsTests.java +++ b/tests/src/com/android/inputmethod/latin/SuggestedWordsTests.java @@ -21,6 +21,8 @@ import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; import android.test.AndroidTestCase; import android.test.suitebuilder.annotation.SmallTest; +import com.android.inputmethod.latin.utils.CollectionUtils; + import java.util.ArrayList; import java.util.Locale; @@ -32,9 +34,14 @@ public class SuggestedWordsTests extends AndroidTestCase { final int NUMBER_OF_ADDED_SUGGESTIONS = 5; final ArrayList<SuggestedWordInfo> list = CollectionUtils.newArrayList(); list.add(new SuggestedWordInfo(TYPED_WORD, TYPED_WORD_FREQ, - SuggestedWordInfo.KIND_TYPED, "")); + SuggestedWordInfo.KIND_TYPED, null /* sourceDict */, + SuggestedWordInfo.NOT_AN_INDEX /* indexOfTouchPointOfSecondWord */, + SuggestedWordInfo.NOT_A_CONFIDENCE /* autoCommitFirstWordConfidence */)); for (int i = 0; i < NUMBER_OF_ADDED_SUGGESTIONS; ++i) { - list.add(new SuggestedWordInfo("" + i, 1, SuggestedWordInfo.KIND_CORRECTION, "")); + list.add(new SuggestedWordInfo("" + i, 1, SuggestedWordInfo.KIND_CORRECTION, + null /* sourceDict */, + SuggestedWordInfo.NOT_AN_INDEX /* indexOfTouchPointOfSecondWord */, + SuggestedWordInfo.NOT_A_CONFIDENCE /* autoCommitFirstWordConfidence */)); } final SuggestedWords words = new SuggestedWords( @@ -57,4 +64,37 @@ public class SuggestedWordsTests extends AndroidTestCase { assertEquals("0", wordsWithoutTyped.getWord(0)); assertEquals(SuggestedWordInfo.KIND_CORRECTION, wordsWithoutTyped.getInfo(0).mKind); } + + // Helper for testGetTransformedWordInfo + private SuggestedWordInfo createWordInfo(final String s) { + // Use 100 as the frequency because the numerical value does not matter as + // long as it's > 1 and < INT_MAX. + return new SuggestedWordInfo(s, 100, + SuggestedWordInfo.KIND_TYPED, null /* sourceDict */, + SuggestedWordInfo.NOT_AN_INDEX /* indexOfTouchPointOfSecondWord */, + SuggestedWordInfo.NOT_A_CONFIDENCE /* autoCommitFirstWordConfidence */); + } + + // Helper for testGetTransformedWordInfo + private SuggestedWordInfo transformWordInfo(final String info, + final int trailingSingleQuotesCount) { + return Suggest.getTransformedSuggestedWordInfo(createWordInfo(info), + Locale.ENGLISH, false /* isAllUpperCase */, false /* isFirstCharCapitalized */, + trailingSingleQuotesCount); + } + + public void testGetTransformedSuggestedWordInfo() { + SuggestedWordInfo result = transformWordInfo("word", 0); + assertEquals(result.mWord, "word"); + result = transformWordInfo("word", 1); + assertEquals(result.mWord, "word'"); + result = transformWordInfo("word", 3); + assertEquals(result.mWord, "word'''"); + result = transformWordInfo("didn't", 0); + assertEquals(result.mWord, "didn't"); + result = transformWordInfo("didn't", 1); + assertEquals(result.mWord, "didn't"); + result = transformWordInfo("didn't", 3); + assertEquals(result.mWord, "didn't''"); + } } diff --git a/tests/src/com/android/inputmethod/latin/UserHistoryDictionaryTests.java b/tests/src/com/android/inputmethod/latin/UserHistoryDictionaryTests.java deleted file mode 100644 index 594ba0e7b..000000000 --- a/tests/src/com/android/inputmethod/latin/UserHistoryDictionaryTests.java +++ /dev/null @@ -1,119 +0,0 @@ -/* - * Copyright (C) 2012 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.latin; - -import android.content.SharedPreferences; -import android.preference.PreferenceManager; -import android.test.AndroidTestCase; -import android.test.suitebuilder.annotation.LargeTest; -import android.util.Log; - -import java.io.File; -import java.util.ArrayList; -import java.util.List; -import java.util.Random; -import java.util.Set; - -/** - * Unit tests for UserHistoryDictionary - */ -@LargeTest -public class UserHistoryDictionaryTests extends AndroidTestCase { - private static final String TAG = UserHistoryDictionaryTests.class.getSimpleName(); - private SharedPreferences mPrefs; - - private static final String[] CHARACTERS = { - "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", - "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z" - }; - - @Override - public void setUp() { - mPrefs = PreferenceManager.getDefaultSharedPreferences(getContext()); - } - - /** - * Generates a random word. - */ - private String generateWord(final int value) { - final int lengthOfChars = CHARACTERS.length; - StringBuilder builder = new StringBuilder(); - long lvalue = Math.abs((long)value); - while (lvalue > 0) { - builder.append(CHARACTERS[(int)(lvalue % lengthOfChars)]); - lvalue /= lengthOfChars; - } - return builder.toString(); - } - - private List<String> generateWords(final int number, final Random random) { - final Set<String> wordSet = CollectionUtils.newHashSet(); - while (wordSet.size() < number) { - wordSet.add(generateWord(random.nextInt())); - } - return new ArrayList<String>(wordSet); - } - - private void addToDict(final UserHistoryDictionary dict, final List<String> words) { - String prevWord = null; - for (String word : words) { - dict.forceAddWordForTest(prevWord, word, true); - prevWord = word; - } - } - - public void testRandomWords() { - File dictFile = null; - try { - Log.d(TAG, "This test can be used for profiling."); - Log.d(TAG, "Usage: please set UserHisotoryDictionary.PROFILE_SAVE_RESTORE to true."); - final int numberOfWords = 1000; - final Random random = new Random(123456); - List<String> words = generateWords(numberOfWords, random); - - final String locale = "testRandomWords"; - final String fileName = "UserHistoryDictionary." + locale + ".dict"; - dictFile = new File(getContext().getFilesDir(), fileName); - final UserHistoryDictionary dict = UserHistoryDictionary.getInstance(getContext(), - locale, mPrefs); - dict.isTest = true; - - addToDict(dict, words); - - try { - Log.d(TAG, "waiting for adding the word ..."); - Thread.sleep(2000); - } catch (InterruptedException e) { - Log.d(TAG, "InterruptedException: " + e); - } - - // write to file - dict.close(); - - try { - Log.d(TAG, "waiting for writing ..."); - Thread.sleep(5000); - } catch (InterruptedException e) { - Log.d(TAG, "InterruptedException: " + e); - } - } finally { - if (dictFile != null) { - dictFile.delete(); - } - } - } -} diff --git a/tests/src/com/android/inputmethod/latin/WordComposerTests.java b/tests/src/com/android/inputmethod/latin/WordComposerTests.java new file mode 100644 index 000000000..1434c6b63 --- /dev/null +++ b/tests/src/com/android/inputmethod/latin/WordComposerTests.java @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2013 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.latin; + +import android.test.AndroidTestCase; +import android.test.suitebuilder.annotation.SmallTest; + +/** + * Unit tests for WordComposer. + */ +@SmallTest +public class WordComposerTests extends AndroidTestCase { + public void testMoveCursor() { + final WordComposer wc = new WordComposer(); + final String STR_WITHIN_BMP = "abcdef"; + wc.setComposingWord(STR_WITHIN_BMP, null); + assertEquals(wc.size(), + STR_WITHIN_BMP.codePointCount(0, STR_WITHIN_BMP.length())); + assertFalse(wc.isCursorFrontOrMiddleOfComposingWord()); + wc.setCursorPositionWithinWord(2); + assertTrue(wc.isCursorFrontOrMiddleOfComposingWord()); + // Move the cursor to after the 'd' + assertTrue(wc.moveCursorByAndReturnIfInsideComposingWord(2)); + assertTrue(wc.isCursorFrontOrMiddleOfComposingWord()); + // Move the cursor to after the 'e' + assertTrue(wc.moveCursorByAndReturnIfInsideComposingWord(1)); + assertTrue(wc.isCursorFrontOrMiddleOfComposingWord()); + assertEquals(wc.size(), 6); + // Move the cursor to after the 'f' + assertTrue(wc.moveCursorByAndReturnIfInsideComposingWord(1)); + assertFalse(wc.isCursorFrontOrMiddleOfComposingWord()); + // Move the cursor past the end of the word + assertFalse(wc.moveCursorByAndReturnIfInsideComposingWord(1)); + assertFalse(wc.moveCursorByAndReturnIfInsideComposingWord(15)); + + // \uD861\uDED7 is 𨛗, a character outside the BMP + final String STR_WITH_SUPPLEMENTARY_CHAR = "abcde\uD861\uDED7fgh"; + wc.setComposingWord(STR_WITH_SUPPLEMENTARY_CHAR, null); + assertEquals(wc.size(), STR_WITH_SUPPLEMENTARY_CHAR.codePointCount(0, + STR_WITH_SUPPLEMENTARY_CHAR.length())); + assertFalse(wc.isCursorFrontOrMiddleOfComposingWord()); + wc.setCursorPositionWithinWord(3); + assertTrue(wc.isCursorFrontOrMiddleOfComposingWord()); + assertTrue(wc.moveCursorByAndReturnIfInsideComposingWord(6)); + assertTrue(wc.isCursorFrontOrMiddleOfComposingWord()); + assertTrue(wc.moveCursorByAndReturnIfInsideComposingWord(1)); + assertFalse(wc.isCursorFrontOrMiddleOfComposingWord()); + + wc.setComposingWord(STR_WITH_SUPPLEMENTARY_CHAR, null); + wc.setCursorPositionWithinWord(3); + assertTrue(wc.moveCursorByAndReturnIfInsideComposingWord(7)); + + wc.setComposingWord(STR_WITH_SUPPLEMENTARY_CHAR, null); + wc.setCursorPositionWithinWord(3); + assertTrue(wc.moveCursorByAndReturnIfInsideComposingWord(7)); + + wc.setComposingWord(STR_WITH_SUPPLEMENTARY_CHAR, null); + wc.setCursorPositionWithinWord(3); + assertTrue(wc.moveCursorByAndReturnIfInsideComposingWord(-3)); + assertFalse(wc.moveCursorByAndReturnIfInsideComposingWord(-1)); + + wc.setComposingWord(STR_WITH_SUPPLEMENTARY_CHAR, null); + wc.setCursorPositionWithinWord(3); + assertFalse(wc.moveCursorByAndReturnIfInsideComposingWord(-9)); + + wc.setComposingWord(STR_WITH_SUPPLEMENTARY_CHAR, null); + assertTrue(wc.moveCursorByAndReturnIfInsideComposingWord(-10)); + + wc.setComposingWord(STR_WITH_SUPPLEMENTARY_CHAR, null); + assertFalse(wc.moveCursorByAndReturnIfInsideComposingWord(-11)); + + wc.setComposingWord(STR_WITH_SUPPLEMENTARY_CHAR, null); + assertTrue(wc.moveCursorByAndReturnIfInsideComposingWord(0)); + + wc.setComposingWord(STR_WITH_SUPPLEMENTARY_CHAR, null); + wc.setCursorPositionWithinWord(2); + assertTrue(wc.moveCursorByAndReturnIfInsideComposingWord(0)); + } +} diff --git a/tests/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderEncoderTests.java b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderEncoderTests.java new file mode 100644 index 000000000..a4d94262f --- /dev/null +++ b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderEncoderTests.java @@ -0,0 +1,684 @@ +/* + * Copyright (C) 2012 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.latin.makedict; + +import android.test.AndroidTestCase; +import android.test.MoreAsserts; +import android.test.suitebuilder.annotation.LargeTest; +import android.util.Log; +import android.util.SparseArray; + +import com.android.inputmethod.latin.makedict.BinaryDictDecoderUtils.CharEncoding; +import com.android.inputmethod.latin.makedict.BinaryDictDecoderUtils.DictBuffer; +import com.android.inputmethod.latin.makedict.FormatSpec.FileHeader; +import com.android.inputmethod.latin.makedict.FormatSpec.FormatOptions; +import com.android.inputmethod.latin.makedict.FusionDictionary.DictionaryOptions; +import com.android.inputmethod.latin.makedict.FusionDictionary.PtNode; +import com.android.inputmethod.latin.makedict.FusionDictionary.PtNodeArray; +import com.android.inputmethod.latin.makedict.FusionDictionary.WeightedString; +import com.android.inputmethod.latin.utils.ByteArrayDictBuffer; +import com.android.inputmethod.latin.utils.CollectionUtils; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map.Entry; +import java.util.Random; +import java.util.Set; +import java.util.TreeMap; + +/** + * Unit tests for BinaryDictDecoderUtils and BinaryDictEncoderUtils. + */ +@LargeTest +public class BinaryDictDecoderEncoderTests extends AndroidTestCase { + private static final String TAG = BinaryDictDecoderEncoderTests.class.getSimpleName(); + private static final int DEFAULT_MAX_UNIGRAMS = 100; + private static final int DEFAULT_CODE_POINT_SET_SIZE = 50; + private static final int UNIGRAM_FREQ = 10; + private static final int BIGRAM_FREQ = 50; + private static final int TOLERANCE_OF_BIGRAM_FREQ = 5; + private static final int NUM_OF_NODES_HAVING_SHORTCUTS = 50; + private static final int NUM_OF_SHORTCUTS = 5; + + private static final int USE_BYTE_ARRAY = 1; + private static final int USE_BYTE_BUFFER = 2; + + private static final ArrayList<String> sWords = CollectionUtils.newArrayList(); + private static final SparseArray<List<Integer>> sEmptyBigrams = + CollectionUtils.newSparseArray(); + private static final SparseArray<List<Integer>> sStarBigrams = CollectionUtils.newSparseArray(); + private static final SparseArray<List<Integer>> sChainBigrams = + CollectionUtils.newSparseArray(); + private static final HashMap<String, List<String>> sShortcuts = CollectionUtils.newHashMap(); + + private static final FormatSpec.FormatOptions VERSION2 = new FormatSpec.FormatOptions(2); + private static final FormatSpec.FormatOptions VERSION3_WITHOUT_DYNAMIC_UPDATE = + new FormatSpec.FormatOptions(3, false /* supportsDynamicUpdate */); + private static final FormatSpec.FormatOptions VERSION3_WITH_DYNAMIC_UPDATE = + new FormatSpec.FormatOptions(3, true /* supportsDynamicUpdate */); + private static final FormatSpec.FormatOptions VERSION4_WITHOUT_DYNAMIC_UPDATE = + new FormatSpec.FormatOptions(4, false /* supportsDynamicUpdate */); + private static final FormatSpec.FormatOptions VERSION4_WITH_DYNAMIC_UPDATE = + new FormatSpec.FormatOptions(4, true /* supportsDynamicUpdate */); + + private static final String TEST_DICT_FILE_EXTENSION = ".testDict"; + + public BinaryDictDecoderEncoderTests() { + this(System.currentTimeMillis(), DEFAULT_MAX_UNIGRAMS); + } + + public BinaryDictDecoderEncoderTests(final long seed, final int maxUnigrams) { + super(); + Log.e(TAG, "Testing dictionary: seed is " + seed); + final Random random = new Random(seed); + sWords.clear(); + final int[] codePointSet = CodePointUtils.generateCodePointSet(DEFAULT_CODE_POINT_SET_SIZE, + random); + generateWords(maxUnigrams, random, codePointSet); + + for (int i = 0; i < sWords.size(); ++i) { + sChainBigrams.put(i, new ArrayList<Integer>()); + if (i > 0) { + sChainBigrams.get(i - 1).add(i); + } + } + + sStarBigrams.put(0, new ArrayList<Integer>()); + for (int i = 1; i < sWords.size(); ++i) { + sStarBigrams.get(0).add(i); + } + + sShortcuts.clear(); + for (int i = 0; i < NUM_OF_NODES_HAVING_SHORTCUTS; ++i) { + final int from = Math.abs(random.nextInt()) % sWords.size(); + sShortcuts.put(sWords.get(from), new ArrayList<String>()); + for (int j = 0; j < NUM_OF_SHORTCUTS; ++j) { + final int to = Math.abs(random.nextInt()) % sWords.size(); + sShortcuts.get(sWords.get(from)).add(sWords.get(to)); + } + } + } + + private DictEncoder getDictEncoder(final File file, final FormatOptions formatOptions) { + if (formatOptions.mVersion == FormatSpec.VERSION4) { + return new Ver4DictEncoder(getContext().getCacheDir()); + } else if (formatOptions.mVersion == 3 || formatOptions.mVersion == 2) { + return new Ver3DictEncoder(file); + } else { + throw new RuntimeException("The format option has a wrong version : " + + formatOptions.mVersion); + } + } + + private void generateWords(final int number, final Random random, final int[] codePointSet) { + final Set<String> wordSet = CollectionUtils.newHashSet(); + while (wordSet.size() < number) { + wordSet.add(CodePointUtils.generateWord(random, codePointSet)); + } + sWords.addAll(wordSet); + } + + /** + * Adds unigrams to the dictionary. + */ + private void addUnigrams(final int number, final FusionDictionary dict, + final List<String> words, final HashMap<String, List<String>> shortcutMap) { + for (int i = 0; i < number; ++i) { + final String word = words.get(i); + final ArrayList<WeightedString> shortcuts = CollectionUtils.newArrayList(); + if (shortcutMap != null && shortcutMap.containsKey(word)) { + for (final String shortcut : shortcutMap.get(word)) { + shortcuts.add(new WeightedString(shortcut, UNIGRAM_FREQ)); + } + } + dict.add(word, UNIGRAM_FREQ, (shortcutMap == null) ? null : shortcuts, + false /* isNotAWord */); + } + } + + private void addBigrams(final FusionDictionary dict, + final List<String> words, + final SparseArray<List<Integer>> bigrams) { + for (int i = 0; i < bigrams.size(); ++i) { + final int w1 = bigrams.keyAt(i); + for (int w2 : bigrams.valueAt(i)) { + dict.setBigram(words.get(w1), words.get(w2), BIGRAM_FREQ); + } + } + } + +// The following is useful to dump the dictionary into a textual file, but it can't compile +// on-device, so it's commented out. +// private void dumpToCombinedFileForDebug(final FusionDictionary dict, final String filename) +// throws IOException { +// com.android.inputmethod.latin.dicttool.CombinedInputOutput.writeDictionaryCombined( +// new java.io.FileWriter(new File(filename)), dict); +// } + + private long timeWritingDictToFile(final File file, final FusionDictionary dict, + final FormatSpec.FormatOptions formatOptions) { + + long now = -1, diff = -1; + + try { + final DictEncoder dictEncoder = getDictEncoder(file, formatOptions); + + now = System.currentTimeMillis(); + // If you need to dump the dict to a textual file, uncomment the line below and the + // function above + // dumpToCombinedFileForDebug(file, "/tmp/foo"); + dictEncoder.writeDictionary(dict, formatOptions); + diff = System.currentTimeMillis() - now; + } catch (IOException e) { + Log.e(TAG, "IO exception while writing file", e); + } catch (UnsupportedFormatException e) { + Log.e(TAG, "UnsupportedFormatException", e); + } + + return diff; + } + + private void checkDictionary(final FusionDictionary dict, final List<String> words, + final SparseArray<List<Integer>> bigrams, + final HashMap<String, List<String>> shortcutMap) { + assertNotNull(dict); + + // check unigram + for (final String word : words) { + final PtNode ptNode = FusionDictionary.findWordInTree(dict.mRootNodeArray, word); + assertNotNull(ptNode); + } + + // check bigram + for (int i = 0; i < bigrams.size(); ++i) { + final int w1 = bigrams.keyAt(i); + for (final int w2 : bigrams.valueAt(i)) { + final PtNode ptNode = FusionDictionary.findWordInTree(dict.mRootNodeArray, + words.get(w1)); + assertNotNull(words.get(w1) + "," + words.get(w2), ptNode.getBigram(words.get(w2))); + } + } + + // check shortcut + if (shortcutMap != null) { + for (final Entry<String, List<String>> entry : shortcutMap.entrySet()) { + assertTrue(words.contains(entry.getKey())); + final PtNode ptNode = FusionDictionary.findWordInTree(dict.mRootNodeArray, + entry.getKey()); + for (final String word : entry.getValue()) { + assertNotNull("shortcut not found: " + entry.getKey() + ", " + word, + ptNode.getShortcut(word)); + } + } + } + } + + private String outputOptions(final int bufferType, + final FormatSpec.FormatOptions formatOptions) { + String result = " : buffer type = " + + ((bufferType == USE_BYTE_BUFFER) ? "byte buffer" : "byte array"); + result += " : version = " + formatOptions.mVersion; + return result + ", supportsDynamicUpdate = " + formatOptions.mSupportsDynamicUpdate; + } + + private DictionaryOptions getDictionaryOptions(final String id, final String version) { + final DictionaryOptions options = new DictionaryOptions(new HashMap<String, String>(), + false, false); + options.mAttributes.put("version", version); + options.mAttributes.put("dictionary", id); + return options; + } + + private File setUpDictionaryFile(final String name, final String version) { + File file = null; + try { + file = new File(getContext().getCacheDir(), name + "." + version + + TEST_DICT_FILE_EXTENSION); + file.createNewFile(); + } catch (IOException e) { + // do nothing + } + assertTrue("Failed to create the dictionary file.", file.exists()); + return file; + } + + private DictDecoder getDictDecoder(final File file, final int bufferType, + final FormatOptions formatOptions, final DictionaryOptions dictOptions) { + if (formatOptions.mVersion == FormatSpec.VERSION4) { + final FileHeader header = new FileHeader(0, dictOptions, formatOptions); + return FormatSpec.getDictDecoder(new File(getContext().getCacheDir(), + header.getId() + "." + header.getVersion()), bufferType); + } else { + return FormatSpec.getDictDecoder(file, bufferType); + } + } + // Tests for readDictionaryBinary and writeDictionaryBinary + + private long timeReadingAndCheckDict(final File file, final List<String> words, + final SparseArray<List<Integer>> bigrams, + final HashMap<String, List<String>> shortcutMap, final int bufferType, + final FormatOptions formatOptions, final DictionaryOptions dictOptions) { + long now, diff = -1; + + FusionDictionary dict = null; + try { + final DictDecoder dictDecoder = getDictDecoder(file, bufferType, formatOptions, + dictOptions); + now = System.currentTimeMillis(); + dict = dictDecoder.readDictionaryBinary(null, false /* deleteDictIfBroken */); + diff = System.currentTimeMillis() - now; + } catch (IOException e) { + Log.e(TAG, "IOException while reading dictionary", e); + } catch (UnsupportedFormatException e) { + Log.e(TAG, "Unsupported format", e); + } + + checkDictionary(dict, words, bigrams, shortcutMap); + return diff; + } + + // Tests for readDictionaryBinary and writeDictionaryBinary + private String runReadAndWrite(final List<String> words, + final SparseArray<List<Integer>> bigrams, final HashMap<String, List<String>> shortcuts, + final int bufferType, final FormatSpec.FormatOptions formatOptions, + final String message) { + + final String dictName = "runReadAndWrite"; + final String dictVersion = Long.toString(System.currentTimeMillis()); + final File file = setUpDictionaryFile(dictName, dictVersion); + + final FusionDictionary dict = new FusionDictionary(new PtNodeArray(), + getDictionaryOptions(dictName, dictVersion)); + addUnigrams(words.size(), dict, words, shortcuts); + addBigrams(dict, words, bigrams); + checkDictionary(dict, words, bigrams, shortcuts); + + final long write = timeWritingDictToFile(file, dict, formatOptions); + final long read = timeReadingAndCheckDict(file, words, bigrams, shortcuts, bufferType, + formatOptions, dict.mOptions); + + return "PROF: read=" + read + "ms, write=" + write + "ms :" + message + + " : " + outputOptions(bufferType, formatOptions); + } + + private void runReadAndWriteTests(final List<String> results, final int bufferType, + final FormatSpec.FormatOptions formatOptions) { + results.add(runReadAndWrite(sWords, sEmptyBigrams, null /* shortcuts */, bufferType, + formatOptions, "unigram")); + results.add(runReadAndWrite(sWords, sChainBigrams, null /* shortcuts */, bufferType, + formatOptions, "chain")); + results.add(runReadAndWrite(sWords, sStarBigrams, null /* shortcuts */, bufferType, + formatOptions, "star")); + results.add(runReadAndWrite(sWords, sEmptyBigrams, sShortcuts, bufferType, formatOptions, + "unigram with shortcuts")); + results.add(runReadAndWrite(sWords, sChainBigrams, sShortcuts, bufferType, formatOptions, + "chain with shortcuts")); + results.add(runReadAndWrite(sWords, sStarBigrams, sShortcuts, bufferType, formatOptions, + "star with shortcuts")); + } + + // Unit test for CharEncoding.readString and CharEncoding.writeString. + public void testCharEncoding() { + // the max length of a word in sWords is less than 50. + // See generateWords. + final byte[] buffer = new byte[50 * 3]; + final DictBuffer dictBuffer = new ByteArrayDictBuffer(buffer); + for (final String word : sWords) { + Log.d("testReadAndWriteString", "write : " + word); + Arrays.fill(buffer, (byte)0); + CharEncoding.writeString(buffer, 0, word); + dictBuffer.position(0); + final String str = CharEncoding.readString(dictBuffer); + assertEquals(word, str); + } + } + + public void testReadAndWriteWithByteBuffer() { + final List<String> results = CollectionUtils.newArrayList(); + + runReadAndWriteTests(results, USE_BYTE_BUFFER, VERSION2); + runReadAndWriteTests(results, USE_BYTE_BUFFER, VERSION3_WITHOUT_DYNAMIC_UPDATE); + runReadAndWriteTests(results, USE_BYTE_BUFFER, VERSION3_WITH_DYNAMIC_UPDATE); + runReadAndWriteTests(results, USE_BYTE_BUFFER, VERSION4_WITHOUT_DYNAMIC_UPDATE); + runReadAndWriteTests(results, USE_BYTE_BUFFER, VERSION4_WITH_DYNAMIC_UPDATE); + + for (final String result : results) { + Log.d(TAG, result); + } + } + + public void testReadAndWriteWithByteArray() { + final List<String> results = CollectionUtils.newArrayList(); + + runReadAndWriteTests(results, USE_BYTE_ARRAY, VERSION2); + runReadAndWriteTests(results, USE_BYTE_ARRAY, VERSION3_WITHOUT_DYNAMIC_UPDATE); + runReadAndWriteTests(results, USE_BYTE_ARRAY, VERSION3_WITH_DYNAMIC_UPDATE); + runReadAndWriteTests(results, USE_BYTE_ARRAY, VERSION4_WITHOUT_DYNAMIC_UPDATE); + runReadAndWriteTests(results, USE_BYTE_ARRAY, VERSION4_WITH_DYNAMIC_UPDATE); + + for (final String result : results) { + Log.d(TAG, result); + } + } + + // Tests for readUnigramsAndBigramsBinary + + private void checkWordMap(final List<String> expectedWords, + final SparseArray<List<Integer>> expectedBigrams, + final TreeMap<Integer, String> resultWords, + final TreeMap<Integer, Integer> resultFrequencies, + final TreeMap<Integer, ArrayList<PendingAttribute>> resultBigrams) { + // check unigrams + final Set<String> actualWordsSet = new HashSet<String>(resultWords.values()); + final Set<String> expectedWordsSet = new HashSet<String>(expectedWords); + assertEquals(actualWordsSet, expectedWordsSet); + + for (int freq : resultFrequencies.values()) { + assertEquals(freq, UNIGRAM_FREQ); + } + + // check bigrams + final HashMap<String, List<String>> expBigrams = new HashMap<String, List<String>>(); + for (int i = 0; i < expectedBigrams.size(); ++i) { + final String word1 = expectedWords.get(expectedBigrams.keyAt(i)); + for (int w2 : expectedBigrams.valueAt(i)) { + if (expBigrams.get(word1) == null) { + expBigrams.put(word1, new ArrayList<String>()); + } + expBigrams.get(word1).add(expectedWords.get(w2)); + } + } + + final HashMap<String, List<String>> actBigrams = new HashMap<String, List<String>>(); + for (Entry<Integer, ArrayList<PendingAttribute>> entry : resultBigrams.entrySet()) { + final String word1 = resultWords.get(entry.getKey()); + final int unigramFreq = resultFrequencies.get(entry.getKey()); + for (PendingAttribute attr : entry.getValue()) { + final String word2 = resultWords.get(attr.mAddress); + if (actBigrams.get(word1) == null) { + actBigrams.put(word1, new ArrayList<String>()); + } + actBigrams.get(word1).add(word2); + + final int bigramFreq = BinaryDictIOUtils.reconstructBigramFrequency( + unigramFreq, attr.mFrequency); + assertTrue(Math.abs(bigramFreq - BIGRAM_FREQ) < TOLERANCE_OF_BIGRAM_FREQ); + } + } + + assertEquals(actBigrams, expBigrams); + } + + private long timeAndCheckReadUnigramsAndBigramsBinary(final File file, final List<String> words, + final SparseArray<List<Integer>> bigrams, final int bufferType, + final FormatOptions formatOptions, final DictionaryOptions dictOptions) { + FileInputStream inStream = null; + + final TreeMap<Integer, String> resultWords = CollectionUtils.newTreeMap(); + final TreeMap<Integer, ArrayList<PendingAttribute>> resultBigrams = + CollectionUtils.newTreeMap(); + final TreeMap<Integer, Integer> resultFreqs = CollectionUtils.newTreeMap(); + + long now = -1, diff = -1; + try { + final DictDecoder dictDecoder = getDictDecoder(file, bufferType, formatOptions, + dictOptions); + now = System.currentTimeMillis(); + dictDecoder.readUnigramsAndBigramsBinary(resultWords, resultFreqs, resultBigrams); + diff = System.currentTimeMillis() - now; + } catch (IOException e) { + Log.e(TAG, "IOException", e); + } catch (UnsupportedFormatException e) { + Log.e(TAG, "UnsupportedFormatException", e); + } finally { + if (inStream != null) { + try { + inStream.close(); + } catch (IOException e) { + // do nothing + } + } + } + + checkWordMap(words, bigrams, resultWords, resultFreqs, resultBigrams); + return diff; + } + + private String runReadUnigramsAndBigramsBinary(final ArrayList<String> words, + final SparseArray<List<Integer>> bigrams, final int bufferType, + final FormatSpec.FormatOptions formatOptions, final String message) { + final String dictName = "runReadUnigrams"; + final String dictVersion = Long.toString(System.currentTimeMillis()); + final File file = setUpDictionaryFile(dictName, dictVersion); + + // making the dictionary from lists of words. + final FusionDictionary dict = new FusionDictionary(new PtNodeArray(), + getDictionaryOptions(dictName, dictVersion)); + addUnigrams(words.size(), dict, words, null /* shortcutMap */); + addBigrams(dict, words, bigrams); + + timeWritingDictToFile(file, dict, formatOptions); + + long wordMap = timeAndCheckReadUnigramsAndBigramsBinary(file, words, bigrams, bufferType, + formatOptions, dict.mOptions); + long fullReading = timeReadingAndCheckDict(file, words, bigrams, null /* shortcutMap */, + bufferType, formatOptions, dict.mOptions); + + return "readDictionaryBinary=" + fullReading + ", readUnigramsAndBigramsBinary=" + wordMap + + " : " + message + " : " + outputOptions(bufferType, formatOptions); + } + + private void runReadUnigramsAndBigramsTests(final ArrayList<String> results, + final int bufferType, final FormatSpec.FormatOptions formatOptions) { + results.add(runReadUnigramsAndBigramsBinary(sWords, sEmptyBigrams, bufferType, + formatOptions, "unigram")); + results.add(runReadUnigramsAndBigramsBinary(sWords, sChainBigrams, bufferType, + formatOptions, "chain")); + results.add(runReadUnigramsAndBigramsBinary(sWords, sStarBigrams, bufferType, + formatOptions, "star")); + } + + public void testReadUnigramsAndBigramsBinaryWithByteBuffer() { + final ArrayList<String> results = CollectionUtils.newArrayList(); + + runReadUnigramsAndBigramsTests(results, USE_BYTE_BUFFER, VERSION2); + runReadUnigramsAndBigramsTests(results, USE_BYTE_BUFFER, VERSION3_WITHOUT_DYNAMIC_UPDATE); + runReadUnigramsAndBigramsTests(results, USE_BYTE_BUFFER, VERSION3_WITH_DYNAMIC_UPDATE); + runReadUnigramsAndBigramsTests(results, USE_BYTE_BUFFER, VERSION4_WITHOUT_DYNAMIC_UPDATE); + runReadUnigramsAndBigramsTests(results, USE_BYTE_BUFFER, VERSION4_WITH_DYNAMIC_UPDATE); + + for (final String result : results) { + Log.d(TAG, result); + } + } + + public void testReadUnigramsAndBigramsBinaryWithByteArray() { + final ArrayList<String> results = CollectionUtils.newArrayList(); + + runReadUnigramsAndBigramsTests(results, USE_BYTE_ARRAY, VERSION2); + runReadUnigramsAndBigramsTests(results, USE_BYTE_ARRAY, VERSION3_WITHOUT_DYNAMIC_UPDATE); + runReadUnigramsAndBigramsTests(results, USE_BYTE_ARRAY, VERSION3_WITH_DYNAMIC_UPDATE); + runReadUnigramsAndBigramsTests(results, USE_BYTE_ARRAY, VERSION4_WITHOUT_DYNAMIC_UPDATE); + runReadUnigramsAndBigramsTests(results, USE_BYTE_ARRAY, VERSION4_WITH_DYNAMIC_UPDATE); + + for (final String result : results) { + Log.d(TAG, result); + } + } + + // Tests for getTerminalPosition + private String getWordFromBinary(final DictDecoder dictDecoder, final int address) { + if (dictDecoder.getPosition() != 0) dictDecoder.setPosition(0); + + FileHeader fileHeader = null; + try { + fileHeader = dictDecoder.readHeader(); + } catch (IOException e) { + return null; + } catch (UnsupportedFormatException e) { + return null; + } + if (fileHeader == null) return null; + return BinaryDictDecoderUtils.getWordAtPosition(dictDecoder, fileHeader.mHeaderSize, + address, fileHeader.mFormatOptions).mWord; + } + + private long checkGetTerminalPosition(final DictDecoder dictDecoder, final String word, + int index, boolean contained) { + final int expectedFrequency = (UNIGRAM_FREQ + index) % 255; + long diff = -1; + int position = -1; + try { + final long now = System.nanoTime(); + position = dictDecoder.getTerminalPosition(word); + diff = System.nanoTime() - now; + } catch (IOException e) { + Log.e(TAG, "IOException while getTerminalPosition", e); + } catch (UnsupportedFormatException e) { + Log.e(TAG, "UnsupportedFormatException while getTerminalPosition", e); + } + + assertEquals(FormatSpec.NOT_VALID_WORD != position, contained); + if (contained) assertEquals(getWordFromBinary(dictDecoder, position), word); + return diff; + } + + private void runGetTerminalPosition(final ArrayList<String> words, + final SparseArray<List<Integer>> bigrams, final int bufferType, + final FormatOptions formatOptions, final String message) { + final String dictName = "testGetTerminalPosition"; + final String dictVersion = Long.toString(System.currentTimeMillis()); + final File file = setUpDictionaryFile(dictName, dictVersion); + + final FusionDictionary dict = new FusionDictionary(new PtNodeArray(), + getDictionaryOptions(dictName, dictVersion)); + addUnigrams(sWords.size(), dict, sWords, null /* shortcutMap */); + addBigrams(dict, words, bigrams); + timeWritingDictToFile(file, dict, formatOptions); + + final DictDecoder dictDecoder = getDictDecoder(file, DictDecoder.USE_BYTEARRAY, + formatOptions, dict.mOptions); + try { + dictDecoder.openDictBuffer(); + } catch (IOException e) { + // ignore + Log.e(TAG, "IOException while opening the buffer", e); + } + assertTrue("Can't get the buffer", dictDecoder.isDictBufferOpen()); + + try { + // too long word + final String longWord = "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz"; + assertEquals(FormatSpec.NOT_VALID_WORD, dictDecoder.getTerminalPosition(longWord)); + + // null + assertEquals(FormatSpec.NOT_VALID_WORD, dictDecoder.getTerminalPosition(null)); + + // empty string + assertEquals(FormatSpec.NOT_VALID_WORD, dictDecoder.getTerminalPosition("")); + } catch (IOException e) { + } catch (UnsupportedFormatException e) { + } + + // Test a word that is contained within the dictionary. + long sum = 0; + for (int i = 0; i < sWords.size(); ++i) { + final long time = checkGetTerminalPosition(dictDecoder, sWords.get(i), i, true); + sum += time == -1 ? 0 : time; + } + Log.d(TAG, "per search : " + (((double)sum) / sWords.size() / 1000000) + " : " + message + + " : " + outputOptions(bufferType, formatOptions)); + + // Test a word that isn't contained within the dictionary. + final Random random = new Random((int)System.currentTimeMillis()); + final int[] codePointSet = CodePointUtils.generateCodePointSet(DEFAULT_CODE_POINT_SET_SIZE, + random); + for (int i = 0; i < 1000; ++i) { + final String word = CodePointUtils.generateWord(random, codePointSet); + if (sWords.indexOf(word) != -1) continue; + checkGetTerminalPosition(dictDecoder, word, i, false); + } + } + + private void runGetTerminalPositionTests(final ArrayList<String> results, final int bufferType, + final FormatOptions formatOptions) { + runGetTerminalPosition(sWords, sEmptyBigrams, bufferType, formatOptions, "unigram"); + } + + public void testGetTerminalPosition() { + final ArrayList<String> results = CollectionUtils.newArrayList(); + + runGetTerminalPositionTests(results, USE_BYTE_ARRAY, VERSION2); + runGetTerminalPositionTests(results, USE_BYTE_ARRAY, VERSION3_WITHOUT_DYNAMIC_UPDATE); + runGetTerminalPositionTests(results, USE_BYTE_ARRAY, VERSION3_WITH_DYNAMIC_UPDATE); + runGetTerminalPositionTests(results, USE_BYTE_ARRAY, VERSION4_WITHOUT_DYNAMIC_UPDATE); + runGetTerminalPositionTests(results, USE_BYTE_ARRAY, VERSION4_WITH_DYNAMIC_UPDATE); + + runGetTerminalPositionTests(results, USE_BYTE_BUFFER, VERSION2); + runGetTerminalPositionTests(results, USE_BYTE_BUFFER, VERSION3_WITHOUT_DYNAMIC_UPDATE); + runGetTerminalPositionTests(results, USE_BYTE_BUFFER, VERSION3_WITH_DYNAMIC_UPDATE); + runGetTerminalPositionTests(results, USE_BYTE_BUFFER, VERSION4_WITHOUT_DYNAMIC_UPDATE); + runGetTerminalPositionTests(results, USE_BYTE_BUFFER, VERSION4_WITH_DYNAMIC_UPDATE); + + for (final String result : results) { + Log.d(TAG, result); + } + } + + public void testDeleteWord() { + final String dictName = "testDeleteWord"; + final String dictVersion = Long.toString(System.currentTimeMillis()); + final File file = setUpDictionaryFile(dictName, dictVersion); + + final FusionDictionary dict = new FusionDictionary(new PtNodeArray(), + new FusionDictionary.DictionaryOptions( + new HashMap<String, String>(), false, false)); + addUnigrams(sWords.size(), dict, sWords, null /* shortcutMap */); + timeWritingDictToFile(file, dict, VERSION3_WITH_DYNAMIC_UPDATE); + + final Ver3DictDecoder dictDecoder = new Ver3DictDecoder(file, DictDecoder.USE_BYTEARRAY); + try { + dictDecoder.openDictBuffer(); + } catch (IOException e) { + // ignore + Log.e(TAG, "IOException while opening the buffer", e); + } + assertTrue("Can't get the buffer", dictDecoder.isDictBufferOpen()); + + try { + MoreAsserts.assertNotEqual(FormatSpec.NOT_VALID_WORD, + dictDecoder.getTerminalPosition(sWords.get(0))); + DynamicBinaryDictIOUtils.deleteWord(dictDecoder, sWords.get(0)); + assertEquals(FormatSpec.NOT_VALID_WORD, + dictDecoder.getTerminalPosition(sWords.get(0))); + + MoreAsserts.assertNotEqual(FormatSpec.NOT_VALID_WORD, + dictDecoder.getTerminalPosition(sWords.get(5))); + DynamicBinaryDictIOUtils.deleteWord(dictDecoder, sWords.get(5)); + assertEquals(FormatSpec.NOT_VALID_WORD, + dictDecoder.getTerminalPosition(sWords.get(5))); + } catch (IOException e) { + } catch (UnsupportedFormatException e) { + } + } +} diff --git a/tests/src/com/android/inputmethod/latin/makedict/BinaryDictIOTests.java b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictIOTests.java deleted file mode 100644 index b704d08b3..000000000 --- a/tests/src/com/android/inputmethod/latin/makedict/BinaryDictIOTests.java +++ /dev/null @@ -1,601 +0,0 @@ -/* - * Copyright (C) 2012 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.latin.makedict; - -import android.test.AndroidTestCase; -import android.test.MoreAsserts; -import android.test.suitebuilder.annotation.LargeTest; -import android.util.Log; -import android.util.SparseArray; - -import com.android.inputmethod.latin.CollectionUtils; -import com.android.inputmethod.latin.UserHistoryDictIOUtils; -import com.android.inputmethod.latin.makedict.BinaryDictInputOutput.FusionDictionaryBufferInterface; -import com.android.inputmethod.latin.makedict.FormatSpec.FileHeader; -import com.android.inputmethod.latin.makedict.FusionDictionary.CharGroup; -import com.android.inputmethod.latin.makedict.FusionDictionary.Node; -import com.android.inputmethod.latin.makedict.FusionDictionary.WeightedString; - -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.nio.ByteBuffer; -import java.nio.channels.FileChannel; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Random; -import java.util.Set; - -/** - * Unit tests for BinaryDictInputOutput - */ -@LargeTest -public class BinaryDictIOTests extends AndroidTestCase { - private static final String TAG = BinaryDictIOTests.class.getSimpleName(); - private static final int MAX_UNIGRAMS = 100; - private static final int UNIGRAM_FREQ = 10; - private static final int BIGRAM_FREQ = 50; - private static final int TOLERANCE_OF_BIGRAM_FREQ = 5; - - private static final int USE_BYTE_ARRAY = 1; - private static final int USE_BYTE_BUFFER = 2; - - private static final List<String> sWords = CollectionUtils.newArrayList(); - private static final SparseArray<List<Integer>> sEmptyBigrams = - CollectionUtils.newSparseArray(); - private static final SparseArray<List<Integer>> sStarBigrams = CollectionUtils.newSparseArray(); - private static final SparseArray<List<Integer>> sChainBigrams = - CollectionUtils.newSparseArray(); - - private static final FormatSpec.FormatOptions VERSION2 = new FormatSpec.FormatOptions(2); - private static final FormatSpec.FormatOptions VERSION3_WITHOUT_DYNAMIC_UPDATE = - new FormatSpec.FormatOptions(3, false /* supportsDynamicUpdate */); - private static final FormatSpec.FormatOptions VERSION3_WITH_DYNAMIC_UPDATE = - new FormatSpec.FormatOptions(3, true /* supportsDynamicUpdate */); - - public BinaryDictIOTests() { - super(); - - final long time = System.currentTimeMillis(); - Log.e(TAG, "Testing dictionary: seed is " + time); - final Random random = new Random(time); - sWords.clear(); - generateWords(MAX_UNIGRAMS, random); - - for (int i = 0; i < sWords.size(); ++i) { - sChainBigrams.put(i, new ArrayList<Integer>()); - if (i > 0) { - sChainBigrams.get(i - 1).add(i); - } - } - - sStarBigrams.put(0, new ArrayList<Integer>()); - for (int i = 1; i < sWords.size(); ++i) { - sStarBigrams.get(0).add(i); - } - } - - // Utilities for test - - /** - * Makes new buffer according to BUFFER_TYPE. - */ - private FusionDictionaryBufferInterface getBuffer(final File file, final int bufferType) { - FileInputStream inStream = null; - try { - inStream = new FileInputStream(file); - if (bufferType == USE_BYTE_ARRAY) { - final byte[] array = new byte[(int)file.length()]; - inStream.read(array); - return new UserHistoryDictIOUtils.ByteArrayWrapper(array); - } else if (bufferType == USE_BYTE_BUFFER){ - final ByteBuffer buffer = inStream.getChannel().map( - FileChannel.MapMode.READ_ONLY, 0, file.length()); - return new BinaryDictInputOutput.ByteBufferWrapper(buffer); - } - } catch (IOException e) { - Log.e(TAG, "IOException while making buffer", e); - } finally { - if (inStream != null) { - try { - inStream.close(); - } catch (IOException e) { - Log.e(TAG, "IOException while closing stream", e); - } - } - } - return null; - } - - /** - * Generates a random word. - */ - private String generateWord(final Random random) { - StringBuilder builder = new StringBuilder("a"); - int count = random.nextInt() % 30; // Arbitrarily 30 chars max - while (count > 0) { - final long r = Math.abs(random.nextInt()); - if (r < 0) continue; - // Don't insert 0~0x20, but insert any other code point. - // Code points are in the range 0~0x10FFFF. - final int candidateCodePoint = (int)(0x20 + r % (Character.MAX_CODE_POINT - 0x20)); - // Code points between MIN_ and MAX_SURROGATE are not valid on their own. - if (candidateCodePoint >= Character.MIN_SURROGATE - && candidateCodePoint <= Character.MAX_SURROGATE) continue; - builder.appendCodePoint(candidateCodePoint); - --count; - } - return builder.toString(); - } - - private void generateWords(final int number, final Random random) { - final Set<String> wordSet = CollectionUtils.newHashSet(); - while (wordSet.size() < number) { - wordSet.add(generateWord(random)); - } - sWords.addAll(wordSet); - } - - /** - * Adds unigrams to the dictionary. - */ - private void addUnigrams(final int number, final FusionDictionary dict, - final List<String> words, final Map<String, List<String>> shortcutMap) { - for (int i = 0; i < number; ++i) { - final String word = words.get(i); - final ArrayList<WeightedString> shortcuts = CollectionUtils.newArrayList(); - if (shortcutMap != null && shortcutMap.containsKey(word)) { - for (final String shortcut : shortcutMap.get(word)) { - shortcuts.add(new WeightedString(shortcut, UNIGRAM_FREQ)); - } - } - dict.add(word, UNIGRAM_FREQ, (shortcutMap == null) ? null : shortcuts, - false /* isNotAWord */); - } - } - - private void addBigrams(final FusionDictionary dict, - final List<String> words, - final SparseArray<List<Integer>> bigrams) { - for (int i = 0; i < bigrams.size(); ++i) { - final int w1 = bigrams.keyAt(i); - for (int w2 : bigrams.valueAt(i)) { - dict.setBigram(words.get(w1), words.get(w2), BIGRAM_FREQ); - } - } - } - - private long timeWritingDictToFile(final File file, final FusionDictionary dict, - final FormatSpec.FormatOptions formatOptions) { - - long now = -1, diff = -1; - - try { - final FileOutputStream out = new FileOutputStream(file); - - now = System.currentTimeMillis(); - BinaryDictInputOutput.writeDictionaryBinary(out, dict, formatOptions); - diff = System.currentTimeMillis() - now; - - out.flush(); - out.close(); - } catch (IOException e) { - Log.e(TAG, "IO exception while writing file", e); - } catch (UnsupportedFormatException e) { - Log.e(TAG, "UnsupportedFormatException", e); - } - - return diff; - } - - private void checkDictionary(final FusionDictionary dict, final List<String> words, - final SparseArray<List<Integer>> bigrams, final Map<String, List<String>> shortcutMap) { - assertNotNull(dict); - - // check unigram - for (final String word : words) { - final CharGroup cg = FusionDictionary.findWordInTree(dict.mRoot, word); - assertNotNull(cg); - } - - // check bigram - for (int i = 0; i < bigrams.size(); ++i) { - final int w1 = bigrams.keyAt(i); - for (final int w2 : bigrams.valueAt(i)) { - final CharGroup cg = FusionDictionary.findWordInTree(dict.mRoot, words.get(w1)); - assertNotNull(words.get(w1) + "," + words.get(w2), cg.getBigram(words.get(w2))); - } - } - - // check shortcut - if (shortcutMap != null) { - for (final Map.Entry<String, List<String>> entry : shortcutMap.entrySet()) { - final CharGroup group = FusionDictionary.findWordInTree(dict.mRoot, entry.getKey()); - for (final String word : entry.getValue()) { - assertNotNull("shortcut not found: " + entry.getKey() + ", " + word, - group.getShortcut(word)); - } - } - } - } - - private String outputOptions(final int bufferType, - final FormatSpec.FormatOptions formatOptions) { - String result = " : buffer type = " - + ((bufferType == USE_BYTE_BUFFER) ? "byte buffer" : "byte array"); - result += " : version = " + formatOptions.mVersion; - return result + ", supportsDynamicUpdate = " + formatOptions.mSupportsDynamicUpdate; - } - - // Tests for readDictionaryBinary and writeDictionaryBinary - - private long timeReadingAndCheckDict(final File file, final List<String> words, - final SparseArray<List<Integer>> bigrams, final Map<String, List<String>> shortcutMap, - final int bufferType) { - long now, diff = -1; - final FusionDictionaryBufferInterface buffer = getBuffer(file, bufferType); - assertNotNull(buffer); - - FusionDictionary dict = null; - try { - now = System.currentTimeMillis(); - dict = BinaryDictInputOutput.readDictionaryBinary(buffer, null); - diff = System.currentTimeMillis() - now; - } catch (IOException e) { - Log.e(TAG, "IOException while reading dictionary", e); - } catch (UnsupportedFormatException e) { - Log.e(TAG, "Unsupported format", e); - } - - checkDictionary(dict, words, bigrams, shortcutMap); - return diff; - } - - // Tests for readDictionaryBinary and writeDictionaryBinary - private String runReadAndWrite(final List<String> words, - final SparseArray<List<Integer>> bigrams, final Map<String, List<String>> shortcuts, - final int bufferType, final FormatSpec.FormatOptions formatOptions, - final String message) { - File file = null; - try { - file = File.createTempFile("runReadAndWrite", ".dict", getContext().getCacheDir()); - } catch (IOException e) { - Log.e(TAG, "IOException", e); - } - assertNotNull(file); - - final FusionDictionary dict = new FusionDictionary(new Node(), - new FusionDictionary.DictionaryOptions(new HashMap<String,String>(), false, false)); - addUnigrams(words.size(), dict, words, shortcuts); - addBigrams(dict, words, bigrams); - checkDictionary(dict, words, bigrams, shortcuts); - - final long write = timeWritingDictToFile(file, dict, formatOptions); - final long read = timeReadingAndCheckDict(file, words, bigrams, shortcuts, bufferType); - - return "PROF: read=" + read + "ms, write=" + write + "ms :" + message - + " : " + outputOptions(bufferType, formatOptions); - } - - private void runReadAndWriteTests(final List<String> results, final int bufferType, - final FormatSpec.FormatOptions formatOptions) { - results.add(runReadAndWrite(sWords, sEmptyBigrams, null /* shortcuts */, bufferType, - formatOptions, "unigram")); - results.add(runReadAndWrite(sWords, sChainBigrams, null /* shortcuts */, bufferType, - formatOptions, "chain")); - results.add(runReadAndWrite(sWords, sStarBigrams, null /* shortcuts */, bufferType, - formatOptions, "star")); - } - - public void testReadAndWriteWithByteBuffer() { - final List<String> results = CollectionUtils.newArrayList(); - - runReadAndWriteTests(results, USE_BYTE_BUFFER, VERSION2); - runReadAndWriteTests(results, USE_BYTE_BUFFER, VERSION3_WITHOUT_DYNAMIC_UPDATE); - runReadAndWriteTests(results, USE_BYTE_BUFFER, VERSION3_WITH_DYNAMIC_UPDATE); - - for (final String result : results) { - Log.d(TAG, result); - } - } - - public void testReadAndWriteWithByteArray() { - final List<String> results = CollectionUtils.newArrayList(); - - runReadAndWriteTests(results, USE_BYTE_ARRAY, VERSION2); - runReadAndWriteTests(results, USE_BYTE_ARRAY, VERSION3_WITHOUT_DYNAMIC_UPDATE); - runReadAndWriteTests(results, USE_BYTE_ARRAY, VERSION3_WITH_DYNAMIC_UPDATE); - - for (final String result : results) { - Log.d(TAG, result); - } - } - - // Tests for readUnigramsAndBigramsBinary - - private void checkWordMap(final List<String> expectedWords, - final SparseArray<List<Integer>> expectedBigrams, - final Map<Integer, String> resultWords, - final Map<Integer, Integer> resultFrequencies, - final Map<Integer, ArrayList<PendingAttribute>> resultBigrams) { - // check unigrams - final Set<String> actualWordsSet = new HashSet<String>(resultWords.values()); - final Set<String> expectedWordsSet = new HashSet<String>(expectedWords); - assertEquals(actualWordsSet, expectedWordsSet); - - for (int freq : resultFrequencies.values()) { - assertEquals(freq, UNIGRAM_FREQ); - } - - // check bigrams - final Map<String, List<String>> expBigrams = new HashMap<String, List<String>>(); - for (int i = 0; i < expectedBigrams.size(); ++i) { - final String word1 = expectedWords.get(expectedBigrams.keyAt(i)); - for (int w2 : expectedBigrams.valueAt(i)) { - if (expBigrams.get(word1) == null) { - expBigrams.put(word1, new ArrayList<String>()); - } - expBigrams.get(word1).add(expectedWords.get(w2)); - } - } - - final Map<String, List<String>> actBigrams = new HashMap<String, List<String>>(); - for (Entry<Integer, ArrayList<PendingAttribute>> entry : resultBigrams.entrySet()) { - final String word1 = resultWords.get(entry.getKey()); - final int unigramFreq = resultFrequencies.get(entry.getKey()); - for (PendingAttribute attr : entry.getValue()) { - final String word2 = resultWords.get(attr.mAddress); - if (actBigrams.get(word1) == null) { - actBigrams.put(word1, new ArrayList<String>()); - } - actBigrams.get(word1).add(word2); - - final int bigramFreq = BinaryDictInputOutput.reconstructBigramFrequency( - unigramFreq, attr.mFrequency); - assertTrue(Math.abs(bigramFreq - BIGRAM_FREQ) < TOLERANCE_OF_BIGRAM_FREQ); - } - } - - assertEquals(actBigrams, expBigrams); - } - - private long timeAndCheckReadUnigramsAndBigramsBinary(final File file, final List<String> words, - final SparseArray<List<Integer>> bigrams, final int bufferType) { - FileInputStream inStream = null; - - final Map<Integer, String> resultWords = CollectionUtils.newTreeMap(); - final Map<Integer, ArrayList<PendingAttribute>> resultBigrams = - CollectionUtils.newTreeMap(); - final Map<Integer, Integer> resultFreqs = CollectionUtils.newTreeMap(); - - long now = -1, diff = -1; - final FusionDictionaryBufferInterface buffer = getBuffer(file, bufferType); - assertNotNull("Can't get buffer.", buffer); - try { - now = System.currentTimeMillis(); - BinaryDictIOUtils.readUnigramsAndBigramsBinary(buffer, resultWords, resultFreqs, - resultBigrams); - diff = System.currentTimeMillis() - now; - } catch (IOException e) { - Log.e(TAG, "IOException", e); - } catch (UnsupportedFormatException e) { - Log.e(TAG, "UnsupportedFormatException", e); - } finally { - if (inStream != null) { - try { - inStream.close(); - } catch (IOException e) { - // do nothing - } - } - } - - checkWordMap(words, bigrams, resultWords, resultFreqs, resultBigrams); - return diff; - } - - private String runReadUnigramsAndBigramsBinary(final List<String> words, - final SparseArray<List<Integer>> bigrams, final int bufferType, - final FormatSpec.FormatOptions formatOptions, final String message) { - File file = null; - try { - file = File.createTempFile("runReadUnigrams", ".dict", getContext().getCacheDir()); - } catch (IOException e) { - Log.e(TAG, "IOException", e); - } - assertNotNull(file); - - // making the dictionary from lists of words. - final FusionDictionary dict = new FusionDictionary(new Node(), - new FusionDictionary.DictionaryOptions( - new HashMap<String, String>(), false, false)); - addUnigrams(words.size(), dict, words, null /* shortcutMap */); - addBigrams(dict, words, bigrams); - - timeWritingDictToFile(file, dict, formatOptions); - - long wordMap = timeAndCheckReadUnigramsAndBigramsBinary(file, words, bigrams, bufferType); - long fullReading = timeReadingAndCheckDict(file, words, bigrams, null /* shortcutMap */, - bufferType); - - return "readDictionaryBinary=" + fullReading + ", readUnigramsAndBigramsBinary=" + wordMap - + " : " + message + " : " + outputOptions(bufferType, formatOptions); - } - - private void runReadUnigramsAndBigramsTests(final List<String> results, final int bufferType, - final FormatSpec.FormatOptions formatOptions) { - results.add(runReadUnigramsAndBigramsBinary(sWords, sEmptyBigrams, bufferType, - formatOptions, "unigram")); - results.add(runReadUnigramsAndBigramsBinary(sWords, sChainBigrams, bufferType, - formatOptions, "chain")); - results.add(runReadUnigramsAndBigramsBinary(sWords, sChainBigrams, bufferType, - formatOptions, "star")); - } - - public void testReadUnigramsAndBigramsBinaryWithByteBuffer() { - final List<String> results = CollectionUtils.newArrayList(); - - runReadUnigramsAndBigramsTests(results, USE_BYTE_BUFFER, VERSION2); - runReadUnigramsAndBigramsTests(results, USE_BYTE_BUFFER, VERSION3_WITHOUT_DYNAMIC_UPDATE); - runReadUnigramsAndBigramsTests(results, USE_BYTE_BUFFER, VERSION3_WITH_DYNAMIC_UPDATE); - - for (final String result : results) { - Log.d(TAG, result); - } - } - - public void testReadUnigramsAndBigramsBinaryWithByteArray() { - final List<String> results = CollectionUtils.newArrayList(); - - runReadUnigramsAndBigramsTests(results, USE_BYTE_ARRAY, VERSION2); - runReadUnigramsAndBigramsTests(results, USE_BYTE_ARRAY, VERSION3_WITHOUT_DYNAMIC_UPDATE); - runReadUnigramsAndBigramsTests(results, USE_BYTE_ARRAY, VERSION3_WITH_DYNAMIC_UPDATE); - - for (final String result : results) { - Log.d(TAG, result); - } - } - - // Tests for getTerminalPosition - private String getWordFromBinary(final FusionDictionaryBufferInterface buffer, - final int address) { - if (buffer.position() != 0) buffer.position(0); - - FileHeader header = null; - try { - header = BinaryDictInputOutput.readHeader(buffer); - } catch (IOException e) { - return null; - } catch (UnsupportedFormatException e) { - return null; - } - if (header == null) return null; - return BinaryDictInputOutput.getWordAtAddress(buffer, header.mHeaderSize, - address - header.mHeaderSize, header.mFormatOptions).mWord; - } - - private long runGetTerminalPosition(final FusionDictionaryBufferInterface buffer, - final String word, int index, boolean contained) { - final int expectedFrequency = (UNIGRAM_FREQ + index) % 255; - long diff = -1; - int position = -1; - try { - final long now = System.nanoTime(); - position = BinaryDictIOUtils.getTerminalPosition(buffer, word); - diff = System.nanoTime() - now; - } catch (IOException e) { - Log.e(TAG, "IOException while getTerminalPosition", e); - } catch (UnsupportedFormatException e) { - Log.e(TAG, "UnsupportedFormatException while getTerminalPosition", e); - } - - assertEquals(FormatSpec.NOT_VALID_WORD != position, contained); - if (contained) assertEquals(getWordFromBinary(buffer, position), word); - return diff; - } - - public void testGetTerminalPosition() { - File file = null; - try { - file = File.createTempFile("testGetTerminalPosition", ".dict", - getContext().getCacheDir()); - } catch (IOException e) { - // do nothing - } - assertNotNull(file); - - final FusionDictionary dict = new FusionDictionary(new Node(), - new FusionDictionary.DictionaryOptions( - new HashMap<String, String>(), false, false)); - addUnigrams(sWords.size(), dict, sWords, null /* shortcutMap */); - timeWritingDictToFile(file, dict, VERSION3_WITH_DYNAMIC_UPDATE); - - final FusionDictionaryBufferInterface buffer = getBuffer(file, USE_BYTE_ARRAY); - - try { - // too long word - final String longWord = "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz"; - assertEquals(FormatSpec.NOT_VALID_WORD, - BinaryDictIOUtils.getTerminalPosition(buffer, longWord)); - - // null - assertEquals(FormatSpec.NOT_VALID_WORD, - BinaryDictIOUtils.getTerminalPosition(buffer, null)); - - // empty string - assertEquals(FormatSpec.NOT_VALID_WORD, - BinaryDictIOUtils.getTerminalPosition(buffer, "")); - } catch (IOException e) { - } catch (UnsupportedFormatException e) { - } - - // Test a word that is contained within the dictionary. - long sum = 0; - for (int i = 0; i < sWords.size(); ++i) { - final long time = runGetTerminalPosition(buffer, sWords.get(i), i, true); - sum += time == -1 ? 0 : time; - } - Log.d(TAG, "per a search : " + (((double)sum) / sWords.size() / 1000000)); - - // Test a word that isn't contained within the dictionary. - final Random random = new Random((int)System.currentTimeMillis()); - for (int i = 0; i < 1000; ++i) { - final String word = generateWord(random); - if (sWords.indexOf(word) != -1) continue; - runGetTerminalPosition(buffer, word, i, false); - } - } - - public void testDeleteWord() { - File file = null; - try { - file = File.createTempFile("testDeleteWord", ".dict", getContext().getCacheDir()); - } catch (IOException e) { - // do nothing - } - assertNotNull(file); - - final FusionDictionary dict = new FusionDictionary(new Node(), - new FusionDictionary.DictionaryOptions( - new HashMap<String, String>(), false, false)); - addUnigrams(sWords.size(), dict, sWords, null /* shortcutMap */); - timeWritingDictToFile(file, dict, VERSION3_WITH_DYNAMIC_UPDATE); - - final FusionDictionaryBufferInterface buffer = getBuffer(file, USE_BYTE_ARRAY); - - try { - MoreAsserts.assertNotEqual(FormatSpec.NOT_VALID_WORD, - BinaryDictIOUtils.getTerminalPosition(buffer, sWords.get(0))); - BinaryDictIOUtils.deleteWord(buffer, sWords.get(0)); - assertEquals(FormatSpec.NOT_VALID_WORD, - BinaryDictIOUtils.getTerminalPosition(buffer, sWords.get(0))); - - MoreAsserts.assertNotEqual(FormatSpec.NOT_VALID_WORD, - BinaryDictIOUtils.getTerminalPosition(buffer, sWords.get(5))); - BinaryDictIOUtils.deleteWord(buffer, sWords.get(5)); - assertEquals(FormatSpec.NOT_VALID_WORD, - BinaryDictIOUtils.getTerminalPosition(buffer, sWords.get(5))); - } catch (IOException e) { - } catch (UnsupportedFormatException e) { - } - } -} diff --git a/tests/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtilsTests.java b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtilsTests.java index 47885f023..a83749499 100644 --- a/tests/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtilsTests.java +++ b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtilsTests.java @@ -21,46 +21,52 @@ import android.test.MoreAsserts; import android.test.suitebuilder.annotation.LargeTest; import android.util.Log; -import com.android.inputmethod.latin.CollectionUtils; -import com.android.inputmethod.latin.makedict.BinaryDictInputOutput.ByteBufferWrapper; -import com.android.inputmethod.latin.makedict.BinaryDictInputOutput.FusionDictionaryBufferInterface; +import com.android.inputmethod.latin.makedict.BinaryDictDecoderUtils.DictBuffer; import com.android.inputmethod.latin.makedict.FormatSpec.FileHeader; -import com.android.inputmethod.latin.makedict.FusionDictionary.Node; +import com.android.inputmethod.latin.makedict.FusionDictionary.PtNodeArray; import com.android.inputmethod.latin.makedict.FusionDictionary.WeightedString; +import com.android.inputmethod.latin.utils.CollectionUtils; import java.io.BufferedOutputStream; import java.io.File; -import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; -import java.io.RandomAccessFile; -import java.nio.channels.FileChannel; import java.util.ArrayList; import java.util.HashMap; import java.util.Random; @LargeTest -public class BinaryDictIOUtilsTests extends AndroidTestCase { +public class BinaryDictIOUtilsTests extends AndroidTestCase { private static final String TAG = BinaryDictIOUtilsTests.class.getSimpleName(); private static final FormatSpec.FormatOptions FORMAT_OPTIONS = new FormatSpec.FormatOptions(3, true); - private static final int MAX_UNIGRAMS = 1500; private static final ArrayList<String> sWords = CollectionUtils.newArrayList(); + public static final int DEFAULT_MAX_UNIGRAMS = 1500; + private final int mMaxUnigrams; + + private static final String TEST_DICT_FILE_EXTENSION = ".testDict"; private static final String[] CHARACTERS = { "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "\u00FC" /* ü */, "\u00E2" /* â */, "\u00F1" /* ñ */, // accented characters "\u4E9C" /* 亜 */, "\u4F0A" /* 伊 */, "\u5B87" /* 宇 */, // kanji - "\uD841\uDE28" /* 𠘨 */, "\uD840\uDC0B" /* 𠀋 */, "\uD861\uDeD7" /* 𨛗 */ // surrogate pair + "\uD841\uDE28" /* 𠘨 */, "\uD840\uDC0B" /* 𠀋 */, "\uD861\uDED7" /* 𨛗 */ // surrogate pair }; public BinaryDictIOUtilsTests() { + // 1500 is the default max unigrams + this(System.currentTimeMillis(), DEFAULT_MAX_UNIGRAMS); + } + + public BinaryDictIOUtilsTests(final long seed, final int maxUnigrams) { super(); - final Random random = new Random(123456); + Log.d(TAG, "Seed for test is " + seed + ", maxUnigrams is " + maxUnigrams); + mMaxUnigrams = maxUnigrams; + final Random random = new Random(seed); sWords.clear(); - for (int i = 0; i < MAX_UNIGRAMS; ++i) { + for (int i = 0; i < maxUnigrams; ++i) { sWords.add(generateWord(random.nextInt())); } } @@ -78,8 +84,8 @@ public class BinaryDictIOUtilsTests extends AndroidTestCase { return builder.toString(); } - private static void printCharGroup(final CharGroupInfo info) { - Log.d(TAG, " CharGroup at " + info.mOriginalAddress); + private static void printPtNode(final PtNodeInfo info) { + Log.d(TAG, " PtNode at " + info.mOriginalAddress); Log.d(TAG, " flags = " + info.mFlags); Log.d(TAG, " parentAddress = " + info.mParentAddress); Log.d(TAG, " characters = " + new String(info.mCharacters, 0, @@ -103,70 +109,75 @@ public class BinaryDictIOUtilsTests extends AndroidTestCase { Log.d(TAG, " end address = " + info.mEndAddress); } - private static void printNode(final FusionDictionaryBufferInterface buffer, + private static void printNode(final Ver3DictDecoder dictDecoder, final FormatSpec.FormatOptions formatOptions) { - Log.d(TAG, "Node at " + buffer.position()); - final int count = BinaryDictInputOutput.readCharGroupCount(buffer); - Log.d(TAG, " charGroupCount = " + count); + final DictBuffer dictBuffer = dictDecoder.getDictBuffer(); + Log.d(TAG, "Node at " + dictBuffer.position()); + final int count = BinaryDictDecoderUtils.readPtNodeCount(dictBuffer); + Log.d(TAG, " ptNodeCount = " + count); for (int i = 0; i < count; ++i) { - final CharGroupInfo currentInfo = BinaryDictInputOutput.readCharGroup(buffer, - buffer.position(), formatOptions); - printCharGroup(currentInfo); + final PtNodeInfo currentInfo = dictDecoder.readPtNode(dictBuffer.position(), + formatOptions); + printPtNode(currentInfo); } if (formatOptions.mSupportsDynamicUpdate) { - final int forwardLinkAddress = buffer.readUnsignedInt24(); + final int forwardLinkAddress = dictBuffer.readUnsignedInt24(); Log.d(TAG, " forwardLinkAddress = " + forwardLinkAddress); } } - private static void printBinaryFile(final FusionDictionaryBufferInterface buffer) + @SuppressWarnings("unused") + private static void printBinaryFile(final Ver3DictDecoder dictDecoder) throws IOException, UnsupportedFormatException { - FileHeader header = BinaryDictInputOutput.readHeader(buffer); - while (buffer.position() < buffer.limit()) { - printNode(buffer, header.mFormatOptions); + final FileHeader fileHeader = dictDecoder.readHeader(); + final DictBuffer dictBuffer = dictDecoder.getDictBuffer(); + while (dictBuffer.position() < dictBuffer.limit()) { + printNode(dictDecoder, fileHeader.mFormatOptions); } } private int getWordPosition(final File file, final String word) { int position = FormatSpec.NOT_VALID_WORD; - FileInputStream inStream = null; + try { - inStream = new FileInputStream(file); - final FusionDictionaryBufferInterface buffer = new ByteBufferWrapper( - inStream.getChannel().map(FileChannel.MapMode.READ_ONLY, 0, file.length())); - position = BinaryDictIOUtils.getTerminalPosition(buffer, word); + final Ver3DictDecoder dictDecoder = new Ver3DictDecoder(file, + DictDecoder.USE_READONLY_BYTEBUFFER); + position = dictDecoder.getTerminalPosition(word); } catch (IOException e) { } catch (UnsupportedFormatException e) { - } finally { - if (inStream != null) { - try { - inStream.close(); - } catch (IOException e) { - // do nothing - } - } } return position; } - private CharGroupInfo findWordFromFile(final File file, final String word) { - FileInputStream inStream = null; - CharGroupInfo info = null; + /** + * Find a word using the DictDecoder. + * + * @param dictDecoder the dict decoder + * @param word the word searched + * @return the found ptNodeInfo + * @throws IOException + * @throws UnsupportedFormatException + */ + private static PtNodeInfo findWordByBinaryDictReader(final DictDecoder dictDecoder, + final String word) throws IOException, UnsupportedFormatException { + int position = dictDecoder.getTerminalPosition(word); + if (position != FormatSpec.NOT_VALID_WORD) { + dictDecoder.setPosition(0); + final FileHeader header = dictDecoder.readHeader(); + dictDecoder.setPosition(position); + return dictDecoder.readPtNode(position, header.mFormatOptions); + } + return null; + } + + private PtNodeInfo findWordFromFile(final File file, final String word) { + final DictDecoder dictDecoder = FormatSpec.getDictDecoder(file); + PtNodeInfo info = null; try { - inStream = new FileInputStream(file); - final FusionDictionaryBufferInterface buffer = new ByteBufferWrapper( - inStream.getChannel().map(FileChannel.MapMode.READ_ONLY, 0, file.length())); - info = BinaryDictIOUtils.findWordFromBuffer(buffer, word); + dictDecoder.openDictBuffer(); + info = findWordByBinaryDictReader(dictDecoder, word); } catch (IOException e) { } catch (UnsupportedFormatException e) { - } finally { - if (inStream != null) { - try { - inStream.close(); - } catch (IOException e) { - // do nothing - } - } } return info; } @@ -175,42 +186,34 @@ public class BinaryDictIOUtilsTests extends AndroidTestCase { private long insertAndCheckWord(final File file, final String word, final int frequency, final boolean exist, final ArrayList<WeightedString> bigrams, final ArrayList<WeightedString> shortcuts) { - RandomAccessFile raFile = null; BufferedOutputStream outStream = null; - FusionDictionaryBufferInterface buffer = null; long amountOfTime = -1; try { - raFile = new RandomAccessFile(file, "rw"); - buffer = new ByteBufferWrapper(raFile.getChannel().map( - FileChannel.MapMode.READ_WRITE, 0, file.length())); + final Ver3DictDecoder dictDecoder = new Ver3DictDecoder(file, + DictDecoder.USE_WRITABLE_BYTEBUFFER); + dictDecoder.openDictBuffer(); outStream = new BufferedOutputStream(new FileOutputStream(file, true)); if (!exist) { assertEquals(FormatSpec.NOT_VALID_WORD, getWordPosition(file, word)); } final long now = System.nanoTime(); - BinaryDictIOUtils.insertWord(buffer, outStream, word, frequency, bigrams, shortcuts, - false, false); + DynamicBinaryDictIOUtils.insertWord(dictDecoder, outStream, word, frequency, bigrams, + shortcuts, false, false); amountOfTime = System.nanoTime() - now; outStream.flush(); MoreAsserts.assertNotEqual(FormatSpec.NOT_VALID_WORD, getWordPosition(file, word)); outStream.close(); - raFile.close(); } catch (IOException e) { + Log.e(TAG, "Raised an IOException while inserting a word", e); } catch (UnsupportedFormatException e) { + Log.e(TAG, "Raised an UnsupportedFormatException error while inserting a word", e); } finally { if (outStream != null) { try { outStream.close(); } catch (IOException e) { - // do nothing - } - } - if (raFile != null) { - try { - raFile.close(); - } catch (IOException e) { - // do nothing + Log.e(TAG, "Failed to close the output stream", e); } } } @@ -218,65 +221,48 @@ public class BinaryDictIOUtilsTests extends AndroidTestCase { } private void deleteWord(final File file, final String word) { - RandomAccessFile raFile = null; - FusionDictionaryBufferInterface buffer = null; try { - raFile = new RandomAccessFile(file, "rw"); - buffer = new ByteBufferWrapper(raFile.getChannel().map( - FileChannel.MapMode.READ_WRITE, 0, file.length())); - BinaryDictIOUtils.deleteWord(buffer, word); + final Ver3DictDecoder dictDecoder = new Ver3DictDecoder(file, + DictDecoder.USE_WRITABLE_BYTEBUFFER); + dictDecoder.openDictBuffer(); + DynamicBinaryDictIOUtils.deleteWord(dictDecoder, word); } catch (IOException e) { } catch (UnsupportedFormatException e) { - } finally { - if (raFile != null) { - try { - raFile.close(); - } catch (IOException e) { - // do nothing - } - } } } private void checkReverseLookup(final File file, final String word, final int position) { - FileInputStream inStream = null; + try { - inStream = new FileInputStream(file); - final FusionDictionaryBufferInterface buffer = new ByteBufferWrapper( - inStream.getChannel().map(FileChannel.MapMode.READ_ONLY, 0, file.length())); - final FileHeader header = BinaryDictInputOutput.readHeader(buffer); - assertEquals(word, BinaryDictInputOutput.getWordAtAddress(buffer, header.mHeaderSize, - position - header.mHeaderSize, header.mFormatOptions).mWord); + final DictDecoder dictDecoder = FormatSpec.getDictDecoder(file); + final FileHeader fileHeader = dictDecoder.readHeader(); + assertEquals(word, + BinaryDictDecoderUtils.getWordAtPosition(dictDecoder, fileHeader.mHeaderSize, + position, fileHeader.mFormatOptions).mWord); } catch (IOException e) { + Log.e(TAG, "Raised an IOException while looking up a word", e); } catch (UnsupportedFormatException e) { - } finally { - if (inStream != null) { - try { - inStream.close(); - } catch (IOException e) { - // do nothing - } - } + Log.e(TAG, "Raised an UnsupportedFormatException error while looking up a word", e); } } public void testInsertWord() { File file = null; try { - file = File.createTempFile("testInsertWord", ".dict", getContext().getCacheDir()); + file = File.createTempFile("testInsertWord", TEST_DICT_FILE_EXTENSION, + getContext().getCacheDir()); } catch (IOException e) { fail("IOException while creating temporary file: " + e); } // set an initial dictionary. - final FusionDictionary dict = new FusionDictionary(new Node(), + final FusionDictionary dict = new FusionDictionary(new PtNodeArray(), new FusionDictionary.DictionaryOptions(new HashMap<String,String>(), false, false)); dict.add("abcd", 10, null, false); try { - final FileOutputStream out = new FileOutputStream(file); - BinaryDictInputOutput.writeDictionaryBinary(out, dict, FORMAT_OPTIONS); - out.close(); + final DictEncoder dictEncoder = new Ver3DictEncoder(file); + dictEncoder.writeDictionary(dict, FORMAT_OPTIONS); } catch (IOException e) { fail("IOException while writing an initial dictionary : " + e); } catch (UnsupportedFormatException e) { @@ -313,22 +299,21 @@ public class BinaryDictIOUtilsTests extends AndroidTestCase { public void testInsertWordWithBigrams() { File file = null; try { - file = File.createTempFile("testInsertWordWithBigrams", ".dict", + file = File.createTempFile("testInsertWordWithBigrams", TEST_DICT_FILE_EXTENSION, getContext().getCacheDir()); } catch (IOException e) { fail("IOException while creating temporary file: " + e); } // set an initial dictionary. - final FusionDictionary dict = new FusionDictionary(new Node(), + final FusionDictionary dict = new FusionDictionary(new PtNodeArray(), new FusionDictionary.DictionaryOptions(new HashMap<String,String>(), false, false)); dict.add("abcd", 10, null, false); dict.add("efgh", 15, null, false); try { - final FileOutputStream out = new FileOutputStream(file); - BinaryDictInputOutput.writeDictionaryBinary(out, dict, FORMAT_OPTIONS); - out.close(); + final DictEncoder dictEncoder = new Ver3DictEncoder(file); + dictEncoder.writeDictionary(dict, FORMAT_OPTIONS); } catch (IOException e) { fail("IOException while writing an initial dictionary : " + e); } catch (UnsupportedFormatException e) { @@ -341,7 +326,7 @@ public class BinaryDictIOUtilsTests extends AndroidTestCase { insertAndCheckWord(file, "banana", 0, false, null, null); insertAndCheckWord(file, "recursive", 60, true, banana, null); - final CharGroupInfo info = findWordFromFile(file, "recursive"); + final PtNodeInfo info = findWordFromFile(file, "recursive"); int bananaPos = getWordPosition(file, "banana"); assertNotNull(info.mBigrams); assertEquals(info.mBigrams.size(), 1); @@ -351,21 +336,21 @@ public class BinaryDictIOUtilsTests extends AndroidTestCase { public void testRandomWords() { File file = null; try { - file = File.createTempFile("testRandomWord", ".dict", getContext().getCacheDir()); + file = File.createTempFile("testRandomWord", TEST_DICT_FILE_EXTENSION, + getContext().getCacheDir()); } catch (IOException e) { } assertNotNull(file); // set an initial dictionary. - final FusionDictionary dict = new FusionDictionary(new Node(), + final FusionDictionary dict = new FusionDictionary(new PtNodeArray(), new FusionDictionary.DictionaryOptions(new HashMap<String, String>(), false, false)); dict.add("initial", 10, null, false); try { - final FileOutputStream out = new FileOutputStream(file); - BinaryDictInputOutput.writeDictionaryBinary(out, dict, FORMAT_OPTIONS); - out.close(); + final DictEncoder dictEncoder = new Ver3DictEncoder(file); + dictEncoder.writeDictionary(dict, FORMAT_OPTIONS); } catch (IOException e) { assertTrue(false); } catch (UnsupportedFormatException e) { @@ -390,6 +375,6 @@ public class BinaryDictIOUtilsTests extends AndroidTestCase { Log.d(TAG, "max = " + ((double)maxTimeToInsert/1000000) + " ms."); Log.d(TAG, "min = " + ((double)minTimeToInsert/1000000) + " ms."); - Log.d(TAG, "avg = " + ((double)sum/MAX_UNIGRAMS/1000000) + " ms."); + Log.d(TAG, "avg = " + ((double)sum/mMaxUnigrams/1000000) + " ms."); } } diff --git a/tests/src/com/android/inputmethod/latin/makedict/CodePointUtils.java b/tests/src/com/android/inputmethod/latin/makedict/CodePointUtils.java new file mode 100644 index 000000000..36b958af8 --- /dev/null +++ b/tests/src/com/android/inputmethod/latin/makedict/CodePointUtils.java @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2013 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.latin.makedict; + +import java.util.Random; + +// Utility methods related with code points used for tests. +public class CodePointUtils { + private CodePointUtils() { + // This utility class is not publicly instantiable. + } + + public static int[] generateCodePointSet(final int codePointSetSize, final Random random) { + final int[] codePointSet = new int[codePointSetSize]; + for (int i = codePointSet.length - 1; i >= 0; ) { + final int r = Math.abs(random.nextInt()); + if (r < 0) continue; + // Don't insert 0~0x20, but insert any other code point. + // Code points are in the range 0~0x10FFFF. + final int candidateCodePoint = 0x20 + r % (Character.MAX_CODE_POINT - 0x20); + // Code points between MIN_ and MAX_SURROGATE are not valid on their own. + if (candidateCodePoint >= Character.MIN_SURROGATE + && candidateCodePoint <= Character.MAX_SURROGATE) continue; + codePointSet[i] = candidateCodePoint; + --i; + } + return codePointSet; + } + + /** + * Generates a random word. + */ + public static String generateWord(final Random random, final int[] codePointSet) { + StringBuilder builder = new StringBuilder(); + // 8 * 4 = 32 chars max, but we do it the following way so as to bias the random toward + // longer words. This should be closer to natural language, and more importantly, it will + // exercise the algorithms in dicttool much more. + final int count = 1 + (Math.abs(random.nextInt()) % 5) + + (Math.abs(random.nextInt()) % 5) + + (Math.abs(random.nextInt()) % 5) + + (Math.abs(random.nextInt()) % 5) + + (Math.abs(random.nextInt()) % 5) + + (Math.abs(random.nextInt()) % 5) + + (Math.abs(random.nextInt()) % 5) + + (Math.abs(random.nextInt()) % 5); + while (builder.length() < count) { + builder.appendCodePoint(codePointSet[Math.abs(random.nextInt()) % codePointSet.length]); + } + return builder.toString(); + } +} diff --git a/tests/src/com/android/inputmethod/latin/makedict/SparseTableTests.java b/tests/src/com/android/inputmethod/latin/makedict/SparseTableTests.java new file mode 100644 index 000000000..132483d5e --- /dev/null +++ b/tests/src/com/android/inputmethod/latin/makedict/SparseTableTests.java @@ -0,0 +1,160 @@ +/* + * Copyright (C) 2013 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.latin.makedict; + +import android.test.AndroidTestCase; +import android.test.suitebuilder.annotation.LargeTest; +import android.util.Log; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.Random; + +/** + * Unit tests for SparseTable. + */ +@LargeTest +public class SparseTableTests extends AndroidTestCase { + private static final String TAG = SparseTableTests.class.getSimpleName(); + + private static final int[] SMALL_INDEX = { SparseTable.NOT_EXIST, 0 }; + private static final int[] BIG_INDEX = { SparseTable.NOT_EXIST, 1, 2, 3, 4, 5, 6, 7}; + + private final Random mRandom; + private final ArrayList<Integer> mRandomIndex; + + private static final int DEFAULT_SIZE = 10000; + private static final int BLOCK_SIZE = 8; + + public SparseTableTests() { + this(System.currentTimeMillis(), DEFAULT_SIZE); + } + + public SparseTableTests(final long seed, final int tableSize) { + super(); + Log.d(TAG, "Seed for test is " + seed + ", size is " + tableSize); + mRandom = new Random(seed); + mRandomIndex = new ArrayList<Integer>(tableSize); + for (int i = 0; i < tableSize; ++i) { + mRandomIndex.add(SparseTable.NOT_EXIST); + } + } + + public void testInitializeWithArray() { + final SparseTable table = new SparseTable(SMALL_INDEX, BIG_INDEX, BLOCK_SIZE); + for (int i = 0; i < 8; ++i) { + assertEquals(SparseTable.NOT_EXIST, table.get(i)); + } + assertEquals(SparseTable.NOT_EXIST, table.get(8)); + for (int i = 9; i < 16; ++i) { + assertEquals(i - 8, table.get(i)); + } + } + + public void testSet() { + final SparseTable table = new SparseTable(16, BLOCK_SIZE); + table.set(3, 6); + table.set(8, 16); + for (int i = 0; i < 16; ++i) { + if (i == 3 || i == 8) { + assertEquals(i * 2, table.get(i)); + } else { + assertEquals(SparseTable.NOT_EXIST, table.get(i)); + } + } + } + + private void generateRandomIndex(final int size, final int prop) { + for (int i = 0; i < DEFAULT_SIZE; ++i) { + if (mRandom.nextInt(100) < prop) { + mRandomIndex.set(i, mRandom.nextInt()); + } else { + mRandomIndex.set(i, SparseTable.NOT_EXIST); + } + } + } + + private void runTestRandomSet() { + final SparseTable table = new SparseTable(DEFAULT_SIZE, BLOCK_SIZE); + int elementCount = 0; + for (int i = 0; i < DEFAULT_SIZE; ++i) { + if (mRandomIndex.get(i) != SparseTable.NOT_EXIST) { + table.set(i, mRandomIndex.get(i)); + elementCount++; + } + } + + Log.d(TAG, "table size = " + table.getLookupTableSize() + " + " + + table.getContentTableSize()); + Log.d(TAG, "the table has " + elementCount + " elements"); + for (int i = 0; i < DEFAULT_SIZE; ++i) { + assertEquals(table.get(i), (int)mRandomIndex.get(i)); + } + + // flush and reload + OutputStream lookupOutStream = null; + OutputStream contentOutStream = null; + InputStream lookupInStream = null; + InputStream contentInStream = null; + try { + final File lookupIndexFile = File.createTempFile("testRandomSet", ".small"); + final File contentFile = File.createTempFile("testRandomSet", ".big"); + lookupOutStream = new FileOutputStream(lookupIndexFile); + contentOutStream = new FileOutputStream(contentFile); + table.write(lookupOutStream, contentOutStream); + lookupInStream = new FileInputStream(lookupIndexFile); + contentInStream = new FileInputStream(contentFile); + final byte[] lookupArray = new byte[(int) lookupIndexFile.length()]; + final byte[] contentArray = new byte[(int) contentFile.length()]; + lookupInStream.read(lookupArray); + contentInStream.read(contentArray); + final SparseTable newTable = new SparseTable(lookupArray, contentArray, BLOCK_SIZE); + for (int i = 0; i < DEFAULT_SIZE; ++i) { + assertEquals(table.get(i), newTable.get(i)); + } + } catch (IOException e) { + Log.d(TAG, "IOException while flushing and realoding", e); + } finally { + if (lookupOutStream != null) { + try { + lookupOutStream.close(); + } catch (IOException e) { + Log.d(TAG, "IOException while closing the stream", e); + } + } + if (contentOutStream != null) { + try { + contentOutStream.close(); + } catch (IOException e) { + Log.d(TAG, "IOException while closing contentStream.", e); + } + } + } + } + + public void testRandomSet() { + for (int i = 0; i <= 100; i += 10) { + generateRandomIndex(DEFAULT_SIZE, i); + runTestRandomSet(); + } + } +} diff --git a/tests/src/com/android/inputmethod/latin/makedict/Ver3DictDecoderTests.java b/tests/src/com/android/inputmethod/latin/makedict/Ver3DictDecoderTests.java new file mode 100644 index 000000000..9611599b9 --- /dev/null +++ b/tests/src/com/android/inputmethod/latin/makedict/Ver3DictDecoderTests.java @@ -0,0 +1,150 @@ +/* + * Copyright (C) 2013 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.latin.makedict; + +import com.android.inputmethod.latin.makedict.BinaryDictDecoderUtils.DictBuffer; +import com.android.inputmethod.latin.makedict.DictDecoder.DictionaryBufferFactory; +import com.android.inputmethod.latin.makedict.DictDecoder.DictionaryBufferFromByteArrayFactory; +import com.android.inputmethod.latin.makedict.DictDecoder. + DictionaryBufferFromReadOnlyByteBufferFactory; +import com.android.inputmethod.latin.makedict.DictDecoder. + DictionaryBufferFromWritableByteBufferFactory; + +import android.test.AndroidTestCase; +import android.util.Log; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; + +/** + * Unit tests for Ver3DictDecoder + */ +public class Ver3DictDecoderTests extends AndroidTestCase { + private static final String TAG = Ver3DictDecoderTests.class.getSimpleName(); + + private final byte[] data = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + + // Utilities for testing + public void writeDataToFile(final File file) { + FileOutputStream outStream = null; + try { + outStream = new FileOutputStream(file); + outStream.write(data); + } catch (IOException e) { + fail ("Can't write data to the test file"); + } finally { + if (outStream != null) { + try { + outStream.close(); + } catch (IOException e) { + Log.e(TAG, "Failed to close the output stream", e); + } + } + } + } + + @SuppressWarnings("null") + public void runTestOpenBuffer(final String testName, final DictionaryBufferFactory factory) { + File testFile = null; + try { + testFile = File.createTempFile(testName, ".tmp", getContext().getCacheDir()); + } catch (IOException e) { + Log.e(TAG, "IOException while the creating temporary file", e); + } + + assertNotNull(testFile); + final Ver3DictDecoder dictDecoder = new Ver3DictDecoder(testFile, factory); + try { + dictDecoder.openDictBuffer(); + } catch (Exception e) { + Log.e(TAG, "Failed to open the buffer", e); + } + + writeDataToFile(testFile); + + try { + dictDecoder.openDictBuffer(); + } catch (Exception e) { + Log.e(TAG, "Raised the exception while opening buffer", e); + } + + assertEquals(testFile.length(), dictDecoder.getDictBuffer().capacity()); + } + + public void testOpenBufferWithByteBuffer() { + runTestOpenBuffer("testOpenBufferWithByteBuffer", + new DictionaryBufferFromReadOnlyByteBufferFactory()); + } + + public void testOpenBufferWithByteArray() { + runTestOpenBuffer("testOpenBufferWithByteArray", + new DictionaryBufferFromByteArrayFactory()); + } + + public void testOpenBufferWithWritableByteBuffer() { + runTestOpenBuffer("testOpenBufferWithWritableByteBuffer", + new DictionaryBufferFromWritableByteBufferFactory()); + } + + @SuppressWarnings("null") + public void runTestGetBuffer(final String testName, final DictionaryBufferFactory factory) { + File testFile = null; + try { + testFile = File.createTempFile(testName, ".tmp", getContext().getCacheDir()); + } catch (IOException e) { + Log.e(TAG, "IOException while the creating temporary file", e); + } + + final Ver3DictDecoder dictDecoder = new Ver3DictDecoder(testFile, factory); + + // the default return value of getBuffer() must be null. + assertNull("the default return value of getBuffer() is not null", + dictDecoder.getDictBuffer()); + + writeDataToFile(testFile); + assertTrue(testFile.exists()); + Log.d(TAG, "file length = " + testFile.length()); + + DictBuffer dictBuffer = null; + try { + dictBuffer = dictDecoder.openAndGetDictBuffer(); + } catch (IOException e) { + Log.e(TAG, "Failed to open and get the buffer", e); + } + assertNotNull("the buffer must not be null", dictBuffer); + + for (int i = 0; i < data.length; ++i) { + assertEquals(data[i], dictBuffer.readUnsignedByte()); + } + } + + public void testGetBufferWithByteBuffer() { + runTestGetBuffer("testGetBufferWithByteBuffer", + new DictionaryBufferFromReadOnlyByteBufferFactory()); + } + + public void testGetBufferWithByteArray() { + runTestGetBuffer("testGetBufferWithByteArray", + new DictionaryBufferFromByteArrayFactory()); + } + + public void testGetBufferWithWritableByteBuffer() { + runTestGetBuffer("testGetBufferWithWritableByteBuffer", + new DictionaryBufferFromWritableByteBufferFactory()); + } +} diff --git a/tests/src/com/android/inputmethod/latin/personalization/UserHistoryDictionaryTests.java b/tests/src/com/android/inputmethod/latin/personalization/UserHistoryDictionaryTests.java new file mode 100644 index 000000000..d605cdb84 --- /dev/null +++ b/tests/src/com/android/inputmethod/latin/personalization/UserHistoryDictionaryTests.java @@ -0,0 +1,246 @@ +/* + * Copyright (C) 2012 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.latin.personalization; + +import android.content.SharedPreferences; +import android.preference.PreferenceManager; +import android.test.AndroidTestCase; +import android.test.suitebuilder.annotation.LargeTest; +import android.util.Log; + +import com.android.inputmethod.latin.ExpandableBinaryDictionary; +import com.android.inputmethod.latin.utils.CollectionUtils; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; +import java.util.Random; +import java.util.Set; +import java.util.concurrent.TimeUnit; + +/** + * Unit tests for UserHistoryDictionary + */ +@LargeTest +public class UserHistoryDictionaryTests extends AndroidTestCase { + private static final String TAG = UserHistoryDictionaryTests.class.getSimpleName(); + private SharedPreferences mPrefs; + + private static final String[] CHARACTERS = { + "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", + "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z" + }; + + private static final int MIN_USER_HISTORY_DICTIONARY_FILE_SIZE = 1000; + private static final int WAIT_TERMINATING_IN_MILLISECONDS = 100; + + @Override + public void setUp() { + mPrefs = PreferenceManager.getDefaultSharedPreferences(getContext()); + } + + /** + * Generates a random word. + */ + private String generateWord(final int value) { + final int lengthOfChars = CHARACTERS.length; + StringBuilder builder = new StringBuilder(); + long lvalue = Math.abs((long)value); + while (lvalue > 0) { + builder.append(CHARACTERS[(int)(lvalue % lengthOfChars)]); + lvalue /= lengthOfChars; + } + return builder.toString(); + } + + private List<String> generateWords(final int number, final Random random) { + final Set<String> wordSet = CollectionUtils.newHashSet(); + while (wordSet.size() < number) { + wordSet.add(generateWord(random.nextInt())); + } + return new ArrayList<String>(wordSet); + } + + private void addToDict(final UserHistoryPredictionDictionary dict, final List<String> words) { + String prevWord = null; + for (String word : words) { + dict.addToPersonalizationPredictionDictionary(prevWord, word, true); + prevWord = word; + } + } + + /** + * @param checksContents if true, checks whether written words are actually in the dictionary + * or not. + */ + private void addAndWriteRandomWords(final String testFilenameSuffix, final int numberOfWords, + final Random random, final boolean checksContents) { + final List<String> words = generateWords(numberOfWords, random); + final UserHistoryPredictionDictionary dict = + PersonalizationHelper.getUserHistoryPredictionDictionary(getContext(), + testFilenameSuffix /* locale */, mPrefs); + // Add random words to the user history dictionary. + addToDict(dict, words); + if (checksContents) { + try { + Thread.sleep(TimeUnit.MILLISECONDS.convert(5L, TimeUnit.SECONDS)); + } catch (InterruptedException e) { + } + // Limit word count to check when using a Java on memory dictionary. + final int wordCountToCheck = + ExpandableBinaryDictionary.ENABLE_BINARY_DICTIONARY_DYNAMIC_UPDATE ? + numberOfWords : 10; + for (int i = 0; i < wordCountToCheck; ++i) { + final String word = words.get(i); + // This may fail as long as we use tryLock on inserting the bigram words + assertTrue(dict.isInDictionaryForTests(word)); + } + } + // write to file. + dict.close(); + } + + public void testRandomWords() { + File dictFile = null; + Log.d(TAG, "This test can be used for profiling."); + Log.d(TAG, "Usage: please set UserHistoryDictionary.PROFILE_SAVE_RESTORE to true."); + final String testFilenameSuffix = "testRandomWords" + System.currentTimeMillis(); + final int numberOfWords = 1000; + final Random random = new Random(123456); + + try { + addAndWriteRandomWords(testFilenameSuffix, numberOfWords, random, + true /* checksContents */); + } finally { + try { + final UserHistoryPredictionDictionary dict = + PersonalizationHelper.getUserHistoryPredictionDictionary(getContext(), + testFilenameSuffix, mPrefs); + Log.d(TAG, "waiting for writing ..."); + dict.shutdownExecutorForTests(); + while (!dict.isTerminatedForTests()) { + Thread.sleep(WAIT_TERMINATING_IN_MILLISECONDS); + } + } catch (InterruptedException e) { + Log.d(TAG, "InterruptedException: " + e); + } + + final String fileName = UserHistoryPredictionDictionary.NAME + "." + testFilenameSuffix + + ExpandableBinaryDictionary.DICT_FILE_EXTENSION; + dictFile = new File(getContext().getFilesDir(), fileName); + + if (dictFile != null) { + assertTrue(dictFile.exists()); + assertTrue(dictFile.length() >= MIN_USER_HISTORY_DICTIONARY_FILE_SIZE); + dictFile.delete(); + } + } + } + + public void testStressTestForSwitchingLanguagesAndAddingWords() { + final int numberOfLanguages = 2; + final int numberOfLanguageSwitching = 80; + final int numberOfWordsInsertedForEachLanguageSwitch = 100; + + final File dictFiles[] = new File[numberOfLanguages]; + final String testFilenameSuffixes[] = new String[numberOfLanguages]; + try { + final Random random = new Random(123456); + + // Create filename suffixes for this test. + for (int i = 0; i < numberOfLanguages; i++) { + testFilenameSuffixes[i] = "testSwitchingLanguages" + i; + final String fileName = UserHistoryPredictionDictionary.NAME + "." + + testFilenameSuffixes[i] + ExpandableBinaryDictionary.DICT_FILE_EXTENSION; + dictFiles[i] = new File(getContext().getFilesDir(), fileName); + } + + final long start = System.currentTimeMillis(); + + for (int i = 0; i < numberOfLanguageSwitching; i++) { + final int index = i % numberOfLanguages; + // Switch languages to testFilenameSuffixes[index]. + addAndWriteRandomWords(testFilenameSuffixes[index], + numberOfWordsInsertedForEachLanguageSwitch, random, + false /* checksContents */); + } + + final long end = System.currentTimeMillis(); + Log.d(TAG, "testStressTestForSwitchingLanguageAndAddingWords took " + + (end - start) + " ms"); + } finally { + try { + Log.d(TAG, "waiting for writing ..."); + for (int i = 0; i < numberOfLanguages; i++) { + final UserHistoryPredictionDictionary dict = + PersonalizationHelper.getUserHistoryPredictionDictionary(getContext(), + testFilenameSuffixes[i], mPrefs); + dict.shutdownExecutorForTests(); + while (!dict.isTerminatedForTests()) { + Thread.sleep(WAIT_TERMINATING_IN_MILLISECONDS); + } + } + } catch (InterruptedException e) { + Log.d(TAG, "InterruptedException: " + e); + } + for (final File file : dictFiles) { + if (file != null) { + assertTrue(file.exists()); + assertTrue(file.length() >= MIN_USER_HISTORY_DICTIONARY_FILE_SIZE); + file.delete(); + } + } + } + } + + public void testAddManyWords() { + File dictFile = null; + final String testFilenameSuffix = "testRandomWords" + System.currentTimeMillis(); + final int numberOfWords = + ExpandableBinaryDictionary.ENABLE_BINARY_DICTIONARY_DYNAMIC_UPDATE ? + 10000 : 1000; + final Random random = new Random(123456); + + UserHistoryPredictionDictionary dict = + PersonalizationHelper.getUserHistoryPredictionDictionary(getContext(), + testFilenameSuffix, mPrefs); + try { + addAndWriteRandomWords(testFilenameSuffix, numberOfWords, random, + true /* checksContents */); + dict.close(); + } finally { + try { + Log.d(TAG, "waiting for writing ..."); + dict.shutdownExecutorForTests(); + while (!dict.isTerminatedForTests()) { + Thread.sleep(WAIT_TERMINATING_IN_MILLISECONDS); + } + } catch (InterruptedException e) { + Log.d(TAG, "InterruptedException: ", e); + } + final String fileName = UserHistoryPredictionDictionary.NAME + "." + testFilenameSuffix + + ExpandableBinaryDictionary.DICT_FILE_EXTENSION; + dictFile = new File(getContext().getFilesDir(), fileName); + if (dictFile != null) { + assertTrue(dictFile.exists()); + assertTrue(dictFile.length() >= MIN_USER_HISTORY_DICTIONARY_FILE_SIZE); + dictFile.delete(); + } + } + } + +} diff --git a/tests/src/com/android/inputmethod/latin/utils/AsyncResultHolderTests.java b/tests/src/com/android/inputmethod/latin/utils/AsyncResultHolderTests.java new file mode 100644 index 000000000..7fd167977 --- /dev/null +++ b/tests/src/com/android/inputmethod/latin/utils/AsyncResultHolderTests.java @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2013 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.latin.utils; + +import android.test.AndroidTestCase; +import android.test.suitebuilder.annotation.MediumTest; +import android.util.Log; + +@MediumTest +public class AsyncResultHolderTests extends AndroidTestCase { + private static final String TAG = AsyncResultHolderTests.class.getSimpleName(); + + private static final int TIMEOUT_IN_MILLISECONDS = 500; + private static final int MARGIN_IN_MILLISECONDS = 250; + private static final int DEFAULT_VALUE = 2; + private static final int SET_VALUE = 1; + + private <T> void setAfterGivenTime(final AsyncResultHolder<T> holder, final T value, + final long time) { + new Thread(new Runnable() { + @Override + public void run() { + try { + Thread.sleep(time); + } catch (InterruptedException e) { + Log.d(TAG, "Exception while sleeping", e); + } + holder.set(value); + } + }).start(); + } + + public void testGetWithoutSet() { + final AsyncResultHolder<Integer> holder = new AsyncResultHolder<Integer>(); + final int resultValue = holder.get(DEFAULT_VALUE, TIMEOUT_IN_MILLISECONDS); + assertEquals(DEFAULT_VALUE, resultValue); + } + + public void testGetBeforeSet() { + final AsyncResultHolder<Integer> holder = new AsyncResultHolder<Integer>(); + setAfterGivenTime(holder, SET_VALUE, TIMEOUT_IN_MILLISECONDS + MARGIN_IN_MILLISECONDS); + final int resultValue = holder.get(DEFAULT_VALUE, TIMEOUT_IN_MILLISECONDS); + assertEquals(DEFAULT_VALUE, resultValue); + } + + public void testGetAfterSet() { + final AsyncResultHolder<Integer> holder = new AsyncResultHolder<Integer>(); + holder.set(SET_VALUE); + final int resultValue = holder.get(DEFAULT_VALUE, TIMEOUT_IN_MILLISECONDS); + assertEquals(SET_VALUE, resultValue); + } + + public void testGetBeforeTimeout() { + final AsyncResultHolder<Integer> holder = new AsyncResultHolder<Integer>(); + setAfterGivenTime(holder, SET_VALUE, TIMEOUT_IN_MILLISECONDS - MARGIN_IN_MILLISECONDS); + final int resultValue = holder.get(DEFAULT_VALUE, TIMEOUT_IN_MILLISECONDS); + assertEquals(SET_VALUE, resultValue); + } +} diff --git a/tests/src/com/android/inputmethod/latin/utils/Base64ReaderTests.java b/tests/src/com/android/inputmethod/latin/utils/Base64ReaderTests.java new file mode 100644 index 000000000..b311f5d37 --- /dev/null +++ b/tests/src/com/android/inputmethod/latin/utils/Base64ReaderTests.java @@ -0,0 +1,225 @@ +/* + * Copyright (C) 2013 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.latin.utils; + +import android.test.AndroidTestCase; +import android.test.suitebuilder.annotation.SmallTest; + +import java.io.EOFException; +import java.io.IOException; +import java.io.LineNumberReader; +import java.io.StringReader; + +@SmallTest +public class Base64ReaderTests extends AndroidTestCase { + private static final String EMPTY_STRING = ""; + private static final String INCOMPLETE_CHAR1 = "Q"; + // Encode 'A'. + private static final String INCOMPLETE_CHAR2 = "QQ"; + // Encode 'A', 'B' + private static final String INCOMPLETE_CHAR3 = "QUI"; + // Encode 'A', 'B', 'C' + private static final String COMPLETE_CHAR4 = "QUJD"; + private static final String ALL_BYTE_PATTERN = + "AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIj\n" + + "JCUmJygpKissLS4vMDEyMzQ1Njc4OTo7PD0+P0BBQkNERUZH\n" + + "SElKS0xNTk9QUVJTVFVWV1hZWltcXV5fYGFiY2RlZmdoaWpr\n" + + "bG1ub3BxcnN0dXZ3eHl6e3x9fn+AgYKDhIWGh4iJiouMjY6P\n" + + "kJGSk5SVlpeYmZqbnJ2en6ChoqOkpaanqKmqq6ytrq+wsbKz\n" + + "tLW2t7i5uru8vb6/wMHCw8TFxsfIycrLzM3Oz9DR0tPU1dbX\n" + + "2Nna29zd3t/g4eLj5OXm5+jp6uvs7e7v8PHy8/T19vf4+fr7\n" + + "/P3+/w=="; + + public void test0CharInt8() { + final Base64Reader reader = new Base64Reader( + new LineNumberReader(new StringReader(EMPTY_STRING))); + try { + reader.readUint8(); + fail("0 char"); + } catch (final EOFException e) { + assertEquals("0 char", 0, reader.getByteCount()); + } catch (final IOException e) { + fail("IOException: " + e); + } + } + + public void test1CharInt8() { + final Base64Reader reader = new Base64Reader( + new LineNumberReader(new StringReader(INCOMPLETE_CHAR1))); + try { + reader.readUint8(); + fail("1 char"); + } catch (final EOFException e) { + assertEquals("1 char", 0, reader.getByteCount()); + } catch (final IOException e) { + fail("IOException: " + e); + } + } + + public void test2CharsInt8() { + final Base64Reader reader = new Base64Reader( + new LineNumberReader(new StringReader(INCOMPLETE_CHAR2))); + try { + final int v1 = reader.readUint8(); + assertEquals("2 chars pos 0", 'A', v1); + reader.readUint8(); + fail("2 chars"); + } catch (final EOFException e) { + assertEquals("2 chars", 1, reader.getByteCount()); + } catch (final IOException e) { + fail("IOException: " + e); + } + } + + public void test3CharsInt8() { + final Base64Reader reader = new Base64Reader( + new LineNumberReader(new StringReader(INCOMPLETE_CHAR3))); + try { + final int v1 = reader.readUint8(); + assertEquals("3 chars pos 0", 'A', v1); + final int v2 = reader.readUint8(); + assertEquals("3 chars pos 1", 'B', v2); + reader.readUint8(); + fail("3 chars"); + } catch (final EOFException e) { + assertEquals("3 chars", 2, reader.getByteCount()); + } catch (final IOException e) { + fail("IOException: " + e); + } + } + + public void test4CharsInt8() { + final Base64Reader reader = new Base64Reader( + new LineNumberReader(new StringReader(COMPLETE_CHAR4))); + try { + final int v1 = reader.readUint8(); + assertEquals("4 chars pos 0", 'A', v1); + final int v2 = reader.readUint8(); + assertEquals("4 chars pos 1", 'B', v2); + final int v3 = reader.readUint8(); + assertEquals("4 chars pos 2", 'C', v3); + reader.readUint8(); + fail("4 chars"); + } catch (final EOFException e) { + assertEquals("4 chars", 3, reader.getByteCount()); + } catch (final IOException e) { + fail("IOException: " + e); + } + } + + public void testAllBytePatternInt8() { + final Base64Reader reader = new Base64Reader( + new LineNumberReader(new StringReader(ALL_BYTE_PATTERN))); + try { + for (int i = 0; i <= 0xff; i++) { + final int v = reader.readUint8(); + assertEquals("value: all byte pattern: pos " + i, i, v); + assertEquals("count: all byte pattern: pos " + i, i + 1, reader.getByteCount()); + } + } catch (final EOFException e) { + assertEquals("all byte pattern", 256, reader.getByteCount()); + } catch (final IOException e) { + fail("IOException: " + e); + } + } + + public void test0CharInt16() { + final Base64Reader reader = new Base64Reader( + new LineNumberReader(new StringReader(EMPTY_STRING))); + try { + reader.readInt16(); + fail("0 char"); + } catch (final EOFException e) { + assertEquals("0 char", 0, reader.getByteCount()); + } catch (final IOException e) { + fail("IOException: " + e); + } + } + + public void test1CharInt16() { + final Base64Reader reader = new Base64Reader( + new LineNumberReader(new StringReader(INCOMPLETE_CHAR1))); + try { + reader.readInt16(); + fail("1 char"); + } catch (final EOFException e) { + assertEquals("1 char", 0, reader.getByteCount()); + } catch (final IOException e) { + fail("IOException: " + e); + } + } + + public void test2CharsInt16() { + final Base64Reader reader = new Base64Reader( + new LineNumberReader(new StringReader(INCOMPLETE_CHAR2))); + try { + reader.readInt16(); + fail("2 chars"); + } catch (final EOFException e) { + assertEquals("2 chars", 1, reader.getByteCount()); + } catch (final IOException e) { + fail("IOException: " + e); + } + } + + public void test3CharsInt16() { + final Base64Reader reader = new Base64Reader( + new LineNumberReader(new StringReader(INCOMPLETE_CHAR3))); + try { + final short v1 = reader.readInt16(); + assertEquals("3 chars pos 0", 'A' << 8 | 'B', v1); + reader.readInt16(); + fail("3 chars"); + } catch (final EOFException e) { + assertEquals("3 chars", 2, reader.getByteCount()); + } catch (final IOException e) { + fail("IOException: " + e); + } + } + + public void test4CharsInt16() { + final Base64Reader reader = new Base64Reader( + new LineNumberReader(new StringReader(COMPLETE_CHAR4))); + try { + final short v1 = reader.readInt16(); + assertEquals("4 chars pos 0", 'A' << 8 | 'B', v1); + reader.readInt16(); + fail("4 chars"); + } catch (final EOFException e) { + assertEquals("4 chars", 3, reader.getByteCount()); + } catch (final IOException e) { + fail("IOException: " + e); + } + } + + public void testAllBytePatternInt16() { + final Base64Reader reader = new Base64Reader( + new LineNumberReader(new StringReader(ALL_BYTE_PATTERN))); + try { + for (int i = 0; i <= 0xff; i += 2) { + final short v = reader.readInt16(); + final short expected = (short)(i << 8 | (i + 1)); + assertEquals("value: all byte pattern: pos " + i, expected, v); + assertEquals("count: all byte pattern: pos " + i, i + 2, reader.getByteCount()); + } + } catch (final EOFException e) { + assertEquals("all byte pattern", 256, reader.getByteCount()); + } catch (final IOException e) { + fail("IOException: " + e); + } + } +} diff --git a/tests/src/com/android/inputmethod/latin/CapsModeUtilsTests.java b/tests/src/com/android/inputmethod/latin/utils/CapsModeUtilsTests.java index 339791d57..cf3bdd680 100644 --- a/tests/src/com/android/inputmethod/latin/CapsModeUtilsTests.java +++ b/tests/src/com/android/inputmethod/latin/utils/CapsModeUtilsTests.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.inputmethod.latin; +package com.android.inputmethod.latin.utils; import android.test.AndroidTestCase; import android.test.suitebuilder.annotation.SmallTest; diff --git a/tests/src/com/android/inputmethod/latin/utils/CsvUtilsTests.java b/tests/src/com/android/inputmethod/latin/utils/CsvUtilsTests.java new file mode 100644 index 000000000..a0fa8fe4b --- /dev/null +++ b/tests/src/com/android/inputmethod/latin/utils/CsvUtilsTests.java @@ -0,0 +1,424 @@ +/* + * Copyright (C) 2013 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.latin.utils; + +import android.test.AndroidTestCase; +import android.test.suitebuilder.annotation.SmallTest; + +import com.android.inputmethod.latin.utils.CsvUtils.CsvParseException; + +import java.util.Arrays; + +@SmallTest +public class CsvUtilsTests extends AndroidTestCase { + public void testUnescape() { + assertEquals("", CsvUtils.unescapeField("")); + assertEquals("text", CsvUtils.unescapeField("text")); // text + assertEquals("", CsvUtils.unescapeField("\"\"")); // "" + assertEquals("\"", CsvUtils.unescapeField("\"\"\"\"")); // """" -> " + assertEquals("text", CsvUtils.unescapeField("\"text\"")); // "text" -> text + assertEquals("\"text", CsvUtils.unescapeField("\"\"\"text\"")); // """text" -> "text + assertEquals("text\"", CsvUtils.unescapeField("\"text\"\"\"")); // "text""" -> text" + assertEquals("te\"xt", CsvUtils.unescapeField("\"te\"\"xt\"")); // "te""xt" -> te"xt + assertEquals("\"text\"", + CsvUtils.unescapeField("\"\"\"text\"\"\"")); // """text""" -> "text" + assertEquals("t\"e\"x\"t", + CsvUtils.unescapeField("\"t\"\"e\"\"x\"\"t\"")); // "t""e""x""t" -> t"e"x"t + } + + public void testUnescapeException() { + try { + final String text = CsvUtils.unescapeField("\""); // " + fail("Unterminated quote: text=" + text); + } catch (final CsvParseException success) { + assertEquals("Unterminated quote", success.getMessage()); + } + try { + final String text = CsvUtils.unescapeField("\"\"\""); // """ + fail("Unterminated quote: text=" + text); + } catch (final CsvParseException success) { + assertEquals("Unterminated quote", success.getMessage()); + } + try { + final String text = CsvUtils.unescapeField("\"\"\"\"\""); // """"" + fail("Unterminated quote: text=" + text); + } catch (final CsvParseException success) { + assertEquals("Unterminated quote", success.getMessage()); + } + try { + final String text = CsvUtils.unescapeField("\"text"); // "text + fail("Unterminated quote: text=" + text); + } catch (final CsvParseException success) { + assertEquals("Unterminated quote", success.getMessage()); + } + try { + final String text = CsvUtils.unescapeField("text\""); // text" + fail("Raw quote in text: text=" + text); + } catch (final CsvParseException success) { + assertEquals("Raw quote in text", success.getMessage()); + } + try { + final String text = CsvUtils.unescapeField("te\"xt"); // te"xt + fail("Raw quote in text: text=" + text); + } catch (final CsvParseException success) { + assertEquals("Raw quote in text", success.getMessage()); + } + try { + final String text = CsvUtils.unescapeField("\"\"text"); // ""text + fail("Raw quote in quoted text: text=" + text); + } catch (final CsvParseException success) { + assertEquals("Raw quote in quoted text", success.getMessage()); + } + try { + final String text = CsvUtils.unescapeField("text\"\""); // text"" + fail("Escaped quote in text: text=" + text); + } catch (final CsvParseException success) { + assertEquals("Escaped quote in text", success.getMessage()); + } + try { + final String text = CsvUtils.unescapeField("te\"\"xt"); // te""xt + fail("Escaped quote in text: text=" + text); + } catch (final CsvParseException success) { + assertEquals("Escaped quote in text", success.getMessage()); + } + try { + final String text = CsvUtils.unescapeField("\"\"text\""); // ""text" + fail("Raw quote in quoted text: text=" + text); + } catch (final CsvParseException success) { + assertEquals("Raw quote in quoted text", success.getMessage()); + } + try { + final String text = CsvUtils.unescapeField("\"text\"\""); // "text"" + fail("Unterminated quote: text=" + text); + } catch (final CsvParseException success) { + assertEquals("Unterminated quote", success.getMessage()); + } + try { + final String text = CsvUtils.unescapeField("\"te\"xt\""); // "te"xt" + fail("Raw quote in quoted text: text=" + text); + } catch (final CsvParseException success) { + assertEquals("Raw quote in quoted text", success.getMessage()); + } + try { + final String text = CsvUtils.unescapeField("\"b,c"); // "b,c + fail("Unterminated quote: text=" + text); + } catch (final CsvParseException success) { + assertEquals("Unterminated quote", success.getMessage()); + } + try { + final String text = CsvUtils.unescapeField("\",\"a\""); // ","a" + fail("Raw quote in quoted text: text=" + text); + } catch (final CsvParseException success) { + assertEquals("Raw quote in quoted text", success.getMessage()); + } + } + + private static <T> void assertArrayEquals(final T[] expected, final T[] actual) { + if (expected == actual) { + return; + } + if (expected == null || actual == null) { + assertEquals(Arrays.toString(expected), Arrays.toString(actual)); + return; + } + if (expected.length != actual.length) { + assertEquals("[length]", Arrays.toString(expected), Arrays.toString(actual)); + return; + } + for (int i = 0; i < expected.length; i++) { + final T e = expected[i]; + final T a = actual[i]; + if (e == a) { + continue; + } + assertEquals("["+i+"]", expected[i], actual[i]); + } + } + + public void testSplit() { + assertArrayEquals(new String[]{""}, CsvUtils.split("")); + assertArrayEquals(new String[]{" "}, CsvUtils.split(" ")); + assertArrayEquals(new String[]{"text"}, CsvUtils.split("text")); + assertArrayEquals(new String[]{" a b "}, CsvUtils.split(" a b ")); + + assertArrayEquals(new String[]{"", ""}, CsvUtils.split(",")); + assertArrayEquals(new String[]{"", "", ""}, CsvUtils.split(",,")); + assertArrayEquals(new String[]{" ", " "}, CsvUtils.split(" , ")); + assertArrayEquals(new String[]{" ", " ", " "}, CsvUtils.split(" , , ")); + assertArrayEquals(new String[]{"a", "b"}, CsvUtils.split("a,b")); + assertArrayEquals(new String[]{" a ", " b "}, CsvUtils.split(" a , b ")); + + assertArrayEquals(new String[]{"text"}, + CsvUtils.split("\"text\"")); // "text" + assertArrayEquals(new String[]{" text "}, + CsvUtils.split("\" text \"")); // "_text_" + + assertArrayEquals(new String[]{""}, + CsvUtils.split("\"\"")); // "" + assertArrayEquals(new String[]{"\""}, + CsvUtils.split("\"\"\"\"")); // """" + assertArrayEquals(new String[]{"", ""}, + CsvUtils.split("\"\",\"\"")); // "","" + assertArrayEquals(new String[]{"\",\""}, + CsvUtils.split("\"\"\",\"\"\"")); // """,""" + assertArrayEquals(new String[]{"\"", "\""}, + CsvUtils.split("\"\"\"\",\"\"\"\"")); // """","""" + assertArrayEquals(new String[]{"\"", "\",\""}, + CsvUtils.split("\"\"\"\",\"\"\",\"\"\"")); // """",""",""" + assertArrayEquals(new String[]{"\",\"", "\""}, + CsvUtils.split("\"\"\",\"\"\",\"\"\"\"")); // """,""","""" + + assertArrayEquals(new String[]{" a ", " b , c "}, + CsvUtils.split(" a ,\" b , c \"")); // _a_,"_b_,_c_" + assertArrayEquals(new String[]{" a ", " b , c ", " d "}, + CsvUtils.split(" a ,\" b , c \", d ")); // _a_,"_b_,_c_",_d_ + } + + public void testSplitException() { + try { + final String[] fields = CsvUtils.split(" \"text\" "); // _"text"_ + fail("Raw quote in text: fields=" + Arrays.toString(fields)); + } catch (final CsvParseException success) { + assertEquals("Raw quote in text", success.getMessage()); + } + try { + final String[] fields = CsvUtils.split(" \" text \" "); // _"_text_"_ + fail("Raw quote in text: fields=" + Arrays.toString(fields)); + } catch (final CsvParseException success) { + assertEquals("Raw quote in text", success.getMessage()); + } + + try { + final String[] fields = CsvUtils.split("a,\"b,"); // a,",b + fail("Unterminated quote: fields=" + Arrays.toString(fields)); + } catch (final CsvParseException success) { + assertEquals("Unterminated quote", success.getMessage()); + } + try { + final String[] fields = CsvUtils.split("a,\"\"\",b"); // a,""",b + fail("Unterminated quote: fields=" + Arrays.toString(fields)); + } catch (final CsvParseException success) { + assertEquals("Unterminated quote", success.getMessage()); + } + try { + final String[] fields = CsvUtils.split("a,\"\"\"\"\",b"); // a,""""",b + fail("Unterminated quote: fields=" + Arrays.toString(fields)); + } catch (final CsvParseException success) { + assertEquals("Unterminated quote", success.getMessage()); + } + try { + final String[] fields = CsvUtils.split("a,\"b,c"); // a,"b,c + fail("Unterminated quote: fields=" + Arrays.toString(fields)); + } catch (final CsvParseException success) { + assertEquals("Unterminated quote", success.getMessage()); + } + try { + final String[] fields = CsvUtils.split("a,\",\"b,c"); // a,","b,c + fail("Raw quote in quoted text: fields=" + Arrays.toString(fields)); + } catch (final CsvParseException success) { + assertEquals("Raw quote in quoted text", success.getMessage()); + } + try { + final String[] fields = CsvUtils.split("a,\",\"b\",\",c"); // a,","b",",c + fail("Raw quote in quoted text: fields=" + Arrays.toString(fields)); + } catch (final CsvParseException success) { + assertEquals("Raw quote in quoted text", success.getMessage()); + } + } + + public void testSplitWithTrimSpaces() { + final int trimSpaces = CsvUtils.SPLIT_FLAGS_TRIM_SPACES; + assertArrayEquals(new String[]{""}, CsvUtils.split(trimSpaces, "")); + assertArrayEquals(new String[]{""}, CsvUtils.split(trimSpaces, " ")); + assertArrayEquals(new String[]{"text"}, CsvUtils.split(trimSpaces, "text")); + assertArrayEquals(new String[]{"a b"}, CsvUtils.split(trimSpaces, " a b ")); + + assertArrayEquals(new String[]{"", ""}, CsvUtils.split(trimSpaces, ",")); + assertArrayEquals(new String[]{"", "", ""}, CsvUtils.split(trimSpaces, ",,")); + assertArrayEquals(new String[]{"", ""}, CsvUtils.split(trimSpaces, " , ")); + assertArrayEquals(new String[]{"", "", ""}, CsvUtils.split(trimSpaces, " , , ")); + assertArrayEquals(new String[]{"a", "b"}, CsvUtils.split(trimSpaces, "a,b")); + assertArrayEquals(new String[]{"a", "b"}, CsvUtils.split(trimSpaces, " a , b ")); + + assertArrayEquals(new String[]{"text"}, + CsvUtils.split(trimSpaces, "\"text\"")); // "text" + assertArrayEquals(new String[]{"text"}, + CsvUtils.split(trimSpaces, " \"text\" ")); // _"text"_ + assertArrayEquals(new String[]{" text "}, + CsvUtils.split(trimSpaces, "\" text \"")); // "_text_" + assertArrayEquals(new String[]{" text "}, + CsvUtils.split(trimSpaces, " \" text \" ")); // _"_text_"_ + assertArrayEquals(new String[]{"a", "b"}, + CsvUtils.split(trimSpaces, " \"a\" , \"b\" ")); // _"a"_,_"b"_ + + assertArrayEquals(new String[]{""}, + CsvUtils.split(trimSpaces, " \"\" ")); // _""_ + assertArrayEquals(new String[]{"\""}, + CsvUtils.split(trimSpaces, " \"\"\"\" ")); // _""""_ + assertArrayEquals(new String[]{"", ""}, + CsvUtils.split(trimSpaces, " \"\" , \"\" ")); // _""_,_""_ + assertArrayEquals(new String[]{"\" , \""}, + CsvUtils.split(trimSpaces, " \"\"\" , \"\"\" ")); // _"""_,_"""_ + assertArrayEquals(new String[]{"\"", "\""}, + CsvUtils.split(trimSpaces, " \"\"\"\" , \"\"\"\" ")); // _""""_,_""""_ + assertArrayEquals(new String[]{"\"", "\" , \""}, + CsvUtils.split(trimSpaces, " \"\"\"\" , \"\"\" , \"\"\" ")); // _""""_,_"""_,_"""_ + assertArrayEquals(new String[]{"\" , \"", "\""}, + CsvUtils.split(trimSpaces, " \"\"\" , \"\"\" , \"\"\"\" ")); // _"""_,_"""_,_""""_ + + assertArrayEquals(new String[]{"a", " b , c "}, + CsvUtils.split(trimSpaces, " a , \" b , c \" ")); // _a_,_"_b_,_c_"_ + assertArrayEquals(new String[]{"a", " b , c ", "d"}, + CsvUtils.split(trimSpaces, " a, \" b , c \" , d ")); // _a,_"_b_,_c_"_,_d_ + } + + public void testEscape() { + assertEquals("", CsvUtils.escapeField("", false)); + assertEquals("plain", CsvUtils.escapeField("plain", false)); + assertEquals(" ", CsvUtils.escapeField(" ", false)); + assertEquals(" ", CsvUtils.escapeField(" ", false)); + assertEquals("a space", CsvUtils.escapeField("a space", false)); + assertEquals(" space-at-start", CsvUtils.escapeField(" space-at-start", false)); + assertEquals("space-at-end ", CsvUtils.escapeField("space-at-end ", false)); + assertEquals("a lot of spaces", CsvUtils.escapeField("a lot of spaces", false)); + assertEquals("\",\"", CsvUtils.escapeField(",", false)); + assertEquals("\",,\"", CsvUtils.escapeField(",,", false)); + assertEquals("\"a,comma\"", CsvUtils.escapeField("a,comma", false)); + assertEquals("\",comma-at-begin\"", CsvUtils.escapeField(",comma-at-begin", false)); + assertEquals("\"comma-at-end,\"", CsvUtils.escapeField("comma-at-end,", false)); + assertEquals("\",,a,lot,,,of,commas,,\"", + CsvUtils.escapeField(",,a,lot,,,of,commas,,", false)); + assertEquals("\"a comma,and a space\"", CsvUtils.escapeField("a comma,and a space", false)); + assertEquals("\"\"\"\"", CsvUtils.escapeField("\"", false)); // " -> """" + assertEquals("\"\"\"\"\"\"", CsvUtils.escapeField("\"\"", false)); // "" -> """""" + assertEquals("\"\"\"\"\"\"\"\"", CsvUtils.escapeField("\"\"\"", false)); // """ -> """""""" + assertEquals("\"\"\"text\"\"\"", + CsvUtils.escapeField("\"text\"", false)); // "text" -> """text""" + assertEquals("\"text has \"\" in middle\"", + CsvUtils.escapeField("text has \" in middle", false)); + assertEquals("\"\"\"quote,at begin\"", CsvUtils.escapeField("\"quote,at begin", false)); + assertEquals("\"quote at,end\"\"\"", CsvUtils.escapeField("quote at,end\"", false)); + assertEquals("\"\"\"quote at begin\"", CsvUtils.escapeField("\"quote at begin", false)); + assertEquals("\"quote at end\"\"\"", CsvUtils.escapeField("quote at end\"", false)); + } + + public void testEscapeWithAlwaysQuoted() { + assertEquals("\"\"", CsvUtils.escapeField("", true)); + assertEquals("\"plain\"", CsvUtils.escapeField("plain", true)); + assertEquals("\" \"", CsvUtils.escapeField(" ", true)); + assertEquals("\" \"", CsvUtils.escapeField(" ", true)); + assertEquals("\"a space\"", CsvUtils.escapeField("a space", true)); + assertEquals("\" space-at-start\"", CsvUtils.escapeField(" space-at-start", true)); + assertEquals("\"space-at-end \"", CsvUtils.escapeField("space-at-end ", true)); + assertEquals("\"a lot of spaces\"", CsvUtils.escapeField("a lot of spaces", true)); + assertEquals("\",\"", CsvUtils.escapeField(",", true)); + assertEquals("\",,\"", CsvUtils.escapeField(",,", true)); + assertEquals("\"a,comma\"", CsvUtils.escapeField("a,comma", true)); + assertEquals("\",comma-at-begin\"", CsvUtils.escapeField(",comma-at-begin", true)); + assertEquals("\"comma-at-end,\"", CsvUtils.escapeField("comma-at-end,", true)); + assertEquals("\",,a,lot,,,of,commas,,\"", + CsvUtils.escapeField(",,a,lot,,,of,commas,,", true)); + assertEquals("\"a comma,and a space\"", CsvUtils.escapeField("a comma,and a space", true)); + assertEquals("\"\"\"\"", CsvUtils.escapeField("\"", true)); // " -> """" + assertEquals("\"\"\"\"\"\"", CsvUtils.escapeField("\"\"", true)); // "" -> """""" + assertEquals("\"\"\"\"\"\"\"\"", CsvUtils.escapeField("\"\"\"", true)); // """ -> """""""" + assertEquals("\"\"\"text\"\"\"", + CsvUtils.escapeField("\"text\"", true)); // "text" -> """text""" + assertEquals("\"text has \"\" in middle\"", + CsvUtils.escapeField("text has \" in middle", true)); + assertEquals("\"\"\"quote,at begin\"", CsvUtils.escapeField("\"quote,at begin", true)); + assertEquals("\"quote at,end\"\"\"", CsvUtils.escapeField("quote at,end\"", true)); + assertEquals("\"\"\"quote at begin\"", CsvUtils.escapeField("\"quote at begin", true)); + assertEquals("\"quote at end\"\"\"", CsvUtils.escapeField("quote at end\"", true)); + } + + public void testJoinWithoutColumnPositions() { + assertEquals("", CsvUtils.join()); + assertEquals("", CsvUtils.join("")); + assertEquals(",", CsvUtils.join("", "")); + + assertEquals("text, text,text ", + CsvUtils.join("text", " text", "text ")); + assertEquals("\"\"\"\",\"\"\"\"\"\",\"\"\"text\"\"\"", + CsvUtils.join("\"", "\"\"", "\"text\"")); + assertEquals("a b,\"c,d\",\"e\"\"f\"", + CsvUtils.join("a b", "c,d", "e\"f")); + } + + public void testJoinWithoutColumnPositionsWithExtraSpace() { + final int extraSpace = CsvUtils.JOIN_FLAGS_EXTRA_SPACE; + assertEquals("", CsvUtils.join(extraSpace)); + assertEquals("", CsvUtils.join(extraSpace, "")); + assertEquals(", ", CsvUtils.join(extraSpace, "", "")); + + assertEquals("text, text, text ", + CsvUtils.join(extraSpace, "text", " text", "text ")); + // ","","text" -> """","""""","""text""" + assertEquals("\"\"\"\", \"\"\"\"\"\", \"\"\"text\"\"\"", + CsvUtils.join(extraSpace, "\"", "\"\"", "\"text\"")); + assertEquals("a b, \"c,d\", \"e\"\"f\"", + CsvUtils.join(extraSpace, "a b", "c,d", "e\"f")); + } + + public void testJoinWithoutColumnPositionsWithExtraSpaceAndAlwaysQuoted() { + final int extrSpaceAndQuoted = + CsvUtils.JOIN_FLAGS_EXTRA_SPACE | CsvUtils.JOIN_FLAGS_ALWAYS_QUOTED; + assertEquals("", CsvUtils.join(extrSpaceAndQuoted)); + assertEquals("\"\"", CsvUtils.join(extrSpaceAndQuoted, "")); + assertEquals("\"\", \"\"", CsvUtils.join(extrSpaceAndQuoted, "", "")); + + assertEquals("\"text\", \" text\", \"text \"", + CsvUtils.join(extrSpaceAndQuoted, "text", " text", "text ")); + // ","","text" -> """", """""", """text""" + assertEquals("\"\"\"\", \"\"\"\"\"\", \"\"\"text\"\"\"", + CsvUtils.join(extrSpaceAndQuoted, "\"", "\"\"", "\"text\"")); + assertEquals("\"a b\", \"c,d\", \"e\"\"f\"", + CsvUtils.join(extrSpaceAndQuoted, "a b", "c,d", "e\"f")); + } + + public void testJoinWithColumnPositions() { + final int noFlags = CsvUtils.JOIN_FLAGS_NONE; + assertEquals("", CsvUtils.join(noFlags, new int[]{})); + assertEquals(" ", CsvUtils.join(noFlags, new int[]{3}, "")); + assertEquals(" ,", CsvUtils.join(noFlags, new int[]{1}, "", "")); + assertEquals(", ", CsvUtils.join(noFlags, new int[]{0, 3}, "", "")); + + assertEquals("text, text, text ", + CsvUtils.join(noFlags, new int[]{0, 8, 15}, "text", " text", "text ")); + // ","","text" -> """", """""","""text""" + assertEquals("\"\"\"\", \"\"\"\"\"\",\"\"\"text\"\"\"", + CsvUtils.join(noFlags, new int[]{0, 8, 15}, "\"", "\"\"", "\"text\"")); + assertEquals("a b, \"c,d\", \"e\"\"f\"", + CsvUtils.join(noFlags, new int[]{0, 8, 15}, "a b", "c,d", "e\"f")); + } + + public void testJoinWithColumnPositionsWithExtraSpace() { + final int extraSpace = CsvUtils.JOIN_FLAGS_EXTRA_SPACE; + assertEquals("", CsvUtils.join(extraSpace, new int[]{})); + assertEquals(" ", CsvUtils.join(extraSpace, new int[]{3}, "")); + assertEquals(" , ", CsvUtils.join(extraSpace, new int[]{1}, "", "")); + assertEquals(", ", CsvUtils.join(extraSpace, new int[]{0, 3}, "", "")); + + assertEquals("text, text, text ", + CsvUtils.join(extraSpace, new int[]{0, 8, 15}, "text", " text", "text ")); + // ","","text" -> """", """""", """text""" + assertEquals("\"\"\"\", \"\"\"\"\"\", \"\"\"text\"\"\"", + CsvUtils.join(extraSpace, new int[]{0, 8, 15}, "\"", "\"\"", "\"text\"")); + assertEquals("a b, \"c,d\", \"e\"\"f\"", + CsvUtils.join(extraSpace, new int[]{0, 8, 15}, "a b", "c,d", "e\"f")); + } +} diff --git a/tests/src/com/android/inputmethod/latin/ForgettingCurveTests.java b/tests/src/com/android/inputmethod/latin/utils/ForgettingCurveTests.java index 3259312df..823bd5d7d 100644 --- a/tests/src/com/android/inputmethod/latin/ForgettingCurveTests.java +++ b/tests/src/com/android/inputmethod/latin/utils/ForgettingCurveTests.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.inputmethod.latin; +package com.android.inputmethod.latin.utils; import android.test.AndroidTestCase; import android.test.suitebuilder.annotation.SmallTest; diff --git a/tests/src/com/android/inputmethod/latin/utils/PrioritizedSerialExecutorTests.java b/tests/src/com/android/inputmethod/latin/utils/PrioritizedSerialExecutorTests.java new file mode 100644 index 000000000..e0755483c --- /dev/null +++ b/tests/src/com/android/inputmethod/latin/utils/PrioritizedSerialExecutorTests.java @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2013 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.latin.utils; + +import android.test.AndroidTestCase; +import android.test.suitebuilder.annotation.MediumTest; +import android.util.Log; + +import java.util.concurrent.atomic.AtomicInteger; + +/** + * Unit tests for PrioritizedSerialExecutor. + * TODO: Add more detailed tests to make use of priorities, etc. + */ +@MediumTest +public class PrioritizedSerialExecutorTests extends AndroidTestCase { + private static final String TAG = PrioritizedSerialExecutorTests.class.getSimpleName(); + + private static final int NUM_OF_TASKS = 10; + private static final int DELAY_FOR_WAITING_TASKS_MILLISECONDS = 500; + + public void testExecute() { + final PrioritizedSerialExecutor executor = new PrioritizedSerialExecutor(); + final AtomicInteger v = new AtomicInteger(0); + for (int i = 0; i < NUM_OF_TASKS; ++i) { + executor.execute(new Runnable() { + @Override + public void run() { + v.incrementAndGet(); + } + }); + } + try { + Thread.sleep(DELAY_FOR_WAITING_TASKS_MILLISECONDS); + } catch (InterruptedException e) { + Log.d(TAG, "Exception while sleeping.", e); + } + + assertEquals(NUM_OF_TASKS, v.get()); + } + + public void testExecutePrioritized() { + final PrioritizedSerialExecutor executor = new PrioritizedSerialExecutor(); + final AtomicInteger v = new AtomicInteger(0); + for (int i = 0; i < NUM_OF_TASKS; ++i) { + executor.executePrioritized(new Runnable() { + @Override + public void run() { + v.incrementAndGet(); + } + }); + } + try { + Thread.sleep(DELAY_FOR_WAITING_TASKS_MILLISECONDS); + } catch (InterruptedException e) { + Log.d(TAG, "Exception while sleeping.", e); + } + + assertEquals(NUM_OF_TASKS, v.get()); + } + + public void testExecuteCombined() { + final PrioritizedSerialExecutor executor = new PrioritizedSerialExecutor(); + final AtomicInteger v = new AtomicInteger(0); + for (int i = 0; i < NUM_OF_TASKS; ++i) { + executor.execute(new Runnable() { + @Override + public void run() { + v.incrementAndGet(); + } + }); + } + + for (int i = 0; i < NUM_OF_TASKS; ++i) { + executor.executePrioritized(new Runnable() { + @Override + public void run() { + v.incrementAndGet(); + } + }); + } + + try { + Thread.sleep(DELAY_FOR_WAITING_TASKS_MILLISECONDS); + } catch (InterruptedException e) { + Log.d(TAG, "Exception while sleeping.", e); + } + + assertEquals(2 * NUM_OF_TASKS, v.get()); + } +} diff --git a/tests/src/com/android/inputmethod/latin/RecapitalizeStatusTests.java b/tests/src/com/android/inputmethod/latin/utils/RecapitalizeStatusTests.java index 9d7203e5a..a52041264 100644 --- a/tests/src/com/android/inputmethod/latin/RecapitalizeStatusTests.java +++ b/tests/src/com/android/inputmethod/latin/utils/RecapitalizeStatusTests.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.inputmethod.latin; +package com.android.inputmethod.latin.utils; import android.test.AndroidTestCase; import android.test.suitebuilder.annotation.SmallTest; diff --git a/tests/src/com/android/inputmethod/latin/ResizableIntArrayTests.java b/tests/src/com/android/inputmethod/latin/utils/ResizableIntArrayTests.java index b9fee950e..cad80d5ce 100644 --- a/tests/src/com/android/inputmethod/latin/ResizableIntArrayTests.java +++ b/tests/src/com/android/inputmethod/latin/utils/ResizableIntArrayTests.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.inputmethod.latin; +package com.android.inputmethod.latin.utils; import android.test.AndroidTestCase; import android.test.suitebuilder.annotation.SmallTest; @@ -340,4 +340,18 @@ public class ResizableIntArrayTests extends AndroidTestCase { expecteds[i + expectedPos], actuals[i + actualPos]); } } + + public void testShift() { + final ResizableIntArray src = new ResizableIntArray(DEFAULT_CAPACITY); + final int limit = DEFAULT_CAPACITY * 10; + final int shiftAmount = 20; + for (int i = 0; i < limit; ++i) { + src.add(i, i); + assertEquals("length after add at " + i, i + 1, src.getLength()); + } + src.shift(shiftAmount); + for (int i = 0; i < limit - shiftAmount; ++i) { + assertEquals("value at " + i, i + shiftAmount, src.get(i)); + } + } } diff --git a/tests/src/com/android/inputmethod/latin/ResourceUtilsTests.java b/tests/src/com/android/inputmethod/latin/utils/ResourceUtilsTests.java index c915522ee..1ae22e307 100644 --- a/tests/src/com/android/inputmethod/latin/ResourceUtilsTests.java +++ b/tests/src/com/android/inputmethod/latin/utils/ResourceUtilsTests.java @@ -14,12 +14,12 @@ * limitations under the License. */ -package com.android.inputmethod.latin; +package com.android.inputmethod.latin.utils; import android.test.AndroidTestCase; import android.test.suitebuilder.annotation.SmallTest; -import com.android.inputmethod.latin.ResourceUtils.DeviceOverridePatternSyntaxError; +import com.android.inputmethod.latin.utils.ResourceUtils.DeviceOverridePatternSyntaxError; import java.util.HashMap; diff --git a/tests/src/com/android/inputmethod/latin/utils/SpannableStringUtilsTests.java b/tests/src/com/android/inputmethod/latin/utils/SpannableStringUtilsTests.java new file mode 100644 index 000000000..fa6ad16c1 --- /dev/null +++ b/tests/src/com/android/inputmethod/latin/utils/SpannableStringUtilsTests.java @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2013 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.latin.utils; + +import android.test.AndroidTestCase; +import android.test.suitebuilder.annotation.SmallTest; +import android.text.style.SuggestionSpan; +import android.text.style.URLSpan; +import android.text.SpannableStringBuilder; +import android.text.Spannable; +import android.text.Spanned; + +@SmallTest +public class SpannableStringUtilsTests extends AndroidTestCase { + public void testConcatWithSuggestionSpansOnly() { + SpannableStringBuilder s = new SpannableStringBuilder("test string\ntest string\n" + + "test string\ntest string\ntest string\ntest string\ntest string\ntest string\n" + + "test string\ntest string\n"); + final int N = 10; + for (int i = 0; i < N; ++i) { + // Put a PARAGRAPH-flagged span that should not be found in the result. + s.setSpan(new SuggestionSpan(getContext(), + new String[] {"" + i}, Spannable.SPAN_PARAGRAPH), + i * 12, i * 12 + 12, Spannable.SPAN_PARAGRAPH); + // Put a normal suggestion span that should be found in the result. + s.setSpan(new SuggestionSpan(getContext(), new String[] {"" + i}, 0), i, i * 2, 0); + // Put a URL span than should not be found in the result. + s.setSpan(new URLSpan("http://a"), i, i * 2, 0); + } + + final CharSequence a = s.subSequence(0, 15); + final CharSequence b = s.subSequence(15, s.length()); + final Spanned result = + (Spanned)SpannableStringUtils.concatWithNonParagraphSuggestionSpansOnly(a, b); + + Object[] spans = result.getSpans(0, result.length(), SuggestionSpan.class); + for (int i = 0; i < spans.length; i++) { + final int flags = result.getSpanFlags(spans[i]); + assertEquals("Should not find a span with PARAGRAPH flag", + flags & Spannable.SPAN_PARAGRAPH, 0); + assertTrue("Should be a SuggestionSpan", spans[i] instanceof SuggestionSpan); + } + } +} diff --git a/tests/src/com/android/inputmethod/latin/StringUtilsTests.java b/tests/src/com/android/inputmethod/latin/utils/StringUtilsTests.java index 4f260987b..4e396a1cf 100644 --- a/tests/src/com/android/inputmethod/latin/StringUtilsTests.java +++ b/tests/src/com/android/inputmethod/latin/utils/StringUtilsTests.java @@ -14,11 +14,15 @@ * limitations under the License. */ -package com.android.inputmethod.latin; +package com.android.inputmethod.latin.utils; + +import com.android.inputmethod.latin.settings.SettingsValues; import android.test.AndroidTestCase; import android.test.suitebuilder.annotation.SmallTest; +import java.util.Arrays; +import java.util.List; import java.util.Locale; @SmallTest @@ -183,6 +187,18 @@ public class StringUtilsTests extends AndroidTestCase { assertTrue(StringUtils.isIdenticalAfterDowncase("")); } + public void testLooksValidForDictionaryInsertion() { + final SettingsValues settings = + SettingsValues.makeDummySettingsValuesForTest(Locale.ENGLISH); + assertTrue(StringUtils.looksValidForDictionaryInsertion("aochaueo", settings)); + assertFalse(StringUtils.looksValidForDictionaryInsertion("", settings)); + assertTrue(StringUtils.looksValidForDictionaryInsertion("ao-ch'aueo", settings)); + assertFalse(StringUtils.looksValidForDictionaryInsertion("2908743256", settings)); + assertTrue(StringUtils.looksValidForDictionaryInsertion("31aochaueo", settings)); + assertFalse(StringUtils.looksValidForDictionaryInsertion("akeo raeoch oerch .", settings)); + assertFalse(StringUtils.looksValidForDictionaryInsertion("!!!", settings)); + } + private static void checkCapitalize(final String src, final String dst, final String separators, final Locale locale) { assertEquals(dst, StringUtils.capitalizeEachWord(src, separators, locale)); @@ -242,4 +258,26 @@ public class StringUtilsTests extends AndroidTestCase { // code for now True is acceptable. assertTrue(StringUtils.lastPartLooksLikeURL(".abc/def")); } + + public void testHexStringUtils() { + final byte[] bytes = new byte[] { (byte)0x01, (byte)0x11, (byte)0x22, (byte)0x33, + (byte)0x55, (byte)0x88, (byte)0xEE }; + final String bytesStr = StringUtils.byteArrayToHexString(bytes); + final byte[] bytes2 = StringUtils.hexStringToByteArray(bytesStr); + for (int i = 0; i < bytes.length; ++i) { + assertTrue(bytes[i] == bytes2[i]); + } + final String bytesStr2 = StringUtils.byteArrayToHexString(bytes2); + assertTrue(bytesStr.equals(bytesStr2)); + } + + public void testJsonStringUtils() { + final Object[] objs = new Object[] { 1, "aaa", "bbb", 3 }; + final List<Object> objArray = Arrays.asList(objs); + final String str = StringUtils.listToJsonStr(objArray); + final List<Object> newObjArray = StringUtils.jsonStrToList(str); + for (int i = 0; i < objs.length; ++i) { + assertEquals(objs[i], newObjArray.get(i)); + } + } } diff --git a/tests/src/com/android/inputmethod/latin/utils/SubtypeLocaleUtilsTests.java b/tests/src/com/android/inputmethod/latin/utils/SubtypeLocaleUtilsTests.java new file mode 100644 index 000000000..856b2dbda --- /dev/null +++ b/tests/src/com/android/inputmethod/latin/utils/SubtypeLocaleUtilsTests.java @@ -0,0 +1,387 @@ +/* + * Copyright (C) 2011 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.latin.utils; + +import android.content.Context; +import android.content.res.Resources; +import android.test.AndroidTestCase; +import android.test.suitebuilder.annotation.SmallTest; +import android.view.inputmethod.InputMethodSubtype; + +import com.android.inputmethod.latin.R; +import com.android.inputmethod.latin.RichInputMethodManager; + +import java.util.ArrayList; +import java.util.Locale; + +@SmallTest +public class SubtypeLocaleUtilsTests extends AndroidTestCase { + // Locale to subtypes list. + private final ArrayList<InputMethodSubtype> mSubtypesList = CollectionUtils.newArrayList(); + + private RichInputMethodManager mRichImm; + private Resources mRes; + + InputMethodSubtype EN_US; + InputMethodSubtype EN_GB; + InputMethodSubtype ES_US; + InputMethodSubtype FR; + InputMethodSubtype FR_CA; + InputMethodSubtype DE; + InputMethodSubtype ZZ; + InputMethodSubtype DE_QWERTY; + InputMethodSubtype FR_QWERTZ; + InputMethodSubtype EN_US_AZERTY; + InputMethodSubtype EN_UK_DVORAK; + InputMethodSubtype ES_US_COLEMAK; + InputMethodSubtype ZZ_AZERTY; + InputMethodSubtype ZZ_PC; + + @Override + protected void setUp() throws Exception { + super.setUp(); + final Context context = getContext(); + RichInputMethodManager.init(context); + mRichImm = RichInputMethodManager.getInstance(); + mRes = context.getResources(); + SubtypeLocaleUtils.init(context); + + EN_US = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet( + Locale.US.toString(), "qwerty"); + EN_GB = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet( + Locale.UK.toString(), "qwerty"); + ES_US = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet( + "es_US", "spanish"); + FR = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet( + Locale.FRENCH.toString(), "azerty"); + FR_CA = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet( + Locale.CANADA_FRENCH.toString(), "qwerty"); + DE = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet( + Locale.GERMAN.toString(), "qwertz"); + ZZ = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet( + SubtypeLocaleUtils.NO_LANGUAGE, "qwerty"); + DE_QWERTY = AdditionalSubtypeUtils.createAdditionalSubtype( + Locale.GERMAN.toString(), "qwerty", null); + FR_QWERTZ = AdditionalSubtypeUtils.createAdditionalSubtype( + Locale.FRENCH.toString(), "qwertz", null); + EN_US_AZERTY = AdditionalSubtypeUtils.createAdditionalSubtype( + Locale.US.toString(), "azerty", null); + EN_UK_DVORAK = AdditionalSubtypeUtils.createAdditionalSubtype( + Locale.UK.toString(), "dvorak", null); + ES_US_COLEMAK = AdditionalSubtypeUtils.createAdditionalSubtype( + "es_US", "colemak", null); + ZZ_AZERTY = AdditionalSubtypeUtils.createAdditionalSubtype( + SubtypeLocaleUtils.NO_LANGUAGE, "azerty", null); + ZZ_PC = AdditionalSubtypeUtils.createAdditionalSubtype( + SubtypeLocaleUtils.NO_LANGUAGE, "pcqwerty", null); + + } + + public void testAllFullDisplayName() { + for (final InputMethodSubtype subtype : mSubtypesList) { + final String subtypeName = + SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(subtype); + if (SubtypeLocaleUtils.isNoLanguage(subtype)) { + final String noLanguage = mRes.getString(R.string.subtype_no_language); + assertTrue(subtypeName, subtypeName.contains(noLanguage)); + } else { + final String languageName = + SubtypeLocaleUtils.getSubtypeLocaleDisplayName(subtype.getLocale()); + assertTrue(subtypeName, subtypeName.contains(languageName)); + } + } + } + + public void testKeyboardLayoutSetName() { + assertEquals("en_US", "qwerty", SubtypeLocaleUtils.getKeyboardLayoutSetName(EN_US)); + assertEquals("en_GB", "qwerty", SubtypeLocaleUtils.getKeyboardLayoutSetName(EN_GB)); + assertEquals("es_US", "spanish", SubtypeLocaleUtils.getKeyboardLayoutSetName(ES_US)); + assertEquals("fr ", "azerty", SubtypeLocaleUtils.getKeyboardLayoutSetName(FR)); + assertEquals("fr_CA", "qwerty", SubtypeLocaleUtils.getKeyboardLayoutSetName(FR_CA)); + assertEquals("de ", "qwertz", SubtypeLocaleUtils.getKeyboardLayoutSetName(DE)); + assertEquals("zz ", "qwerty", SubtypeLocaleUtils.getKeyboardLayoutSetName(ZZ)); + } + + // InputMethodSubtype's display name in system locale (en_US). + // isAdditionalSubtype (T=true, F=false) + // locale layout | display name + // ------ ------- - ---------------------- + // en_US qwerty F English (US) exception + // en_GB qwerty F English (UK) exception + // es_US spanish F Spanish (US) exception + // fr azerty F French + // fr_CA qwerty F French (Canada) + // de qwertz F German + // zz qwerty F Alphabet (QWERTY) + // fr qwertz T French (QWERTZ) + // de qwerty T German (QWERTY) + // en_US azerty T English (US) (AZERTY) exception + // en_UK dvorak T English (UK) (Dvorak) exception + // es_US colemak T Spanish (US) (Colemak) exception + // zz pc T Alphabet (PC) + + public void testPredefinedSubtypesInEnglishSystemLocale() { + final RunInLocale<Void> tests = new RunInLocale<Void>() { + @Override + protected Void job(final Resources res) { + assertEquals("en_US", "English (US)", + SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(EN_US)); + assertEquals("en_GB", "English (UK)", + SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(EN_GB)); + assertEquals("es_US", "Spanish (US)", + SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(ES_US)); + assertEquals("fr ", "French", + SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(FR)); + assertEquals("fr_CA", "French (Canada)", + SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(FR_CA)); + assertEquals("de ", "German", + SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(DE)); + assertEquals("zz ", "Alphabet (QWERTY)", + SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(ZZ)); + return null; + } + }; + tests.runInLocale(mRes, Locale.ENGLISH); + } + + public void testAdditionalSubtypesInEnglishSystemLocale() { + final RunInLocale<Void> tests = new RunInLocale<Void>() { + @Override + protected Void job(final Resources res) { + assertEquals("fr qwertz", "French (QWERTZ)", + SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(FR_QWERTZ)); + assertEquals("de qwerty", "German (QWERTY)", + SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(DE_QWERTY)); + assertEquals("en_US azerty", "English (US) (AZERTY)", + SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(EN_US_AZERTY)); + assertEquals("en_UK dvorak", "English (UK) (Dvorak)", + SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(EN_UK_DVORAK)); + assertEquals("es_US colemak","Spanish (US) (Colemak)", + SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(ES_US_COLEMAK)); + assertEquals("zz pc", "Alphabet (PC)", + SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(ZZ_PC)); + return null; + } + }; + tests.runInLocale(mRes, Locale.ENGLISH); + } + + // InputMethodSubtype's display name in system locale (fr). + // isAdditionalSubtype (T=true, F=false) + // locale layout | display name + // ------ ------- - ---------------------- + // en_US qwerty F Anglais (États-Unis) exception + // en_GB qwerty F Anglais (Royaume-Uni) exception + // es_US spanish F Espagnol (États-Unis) exception + // fr azerty F Français + // fr_CA qwerty F Français (Canada) + // de qwertz F Allemand + // zz qwerty F Aucune langue (QWERTY) + // fr qwertz T Français (QWERTZ) + // de qwerty T Allemand (QWERTY) + // en_US azerty T Anglais (États-Unis) (AZERTY) exception + // en_UK dvorak T Anglais (Royaume-Uni) (Dvorak) exception + // es_US colemak T Espagnol (États-Unis) (Colemak) exception + // zz pc T Alphabet (PC) + + public void testPredefinedSubtypesInFrenchSystemLocale() { + final RunInLocale<Void> tests = new RunInLocale<Void>() { + @Override + protected Void job(final Resources res) { + assertEquals("en_US", "Anglais (États-Unis)", + SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(EN_US)); + assertEquals("en_GB", "Anglais (Royaume-Uni)", + SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(EN_GB)); + assertEquals("es_US", "Espagnol (États-Unis)", + SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(ES_US)); + assertEquals("fr ", "Français", + SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(FR)); + assertEquals("fr_CA", "Français (Canada)", + SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(FR_CA)); + assertEquals("de ", "Allemand", + SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(DE)); + assertEquals("zz ", "Alphabet latin (QWERTY)", + SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(ZZ)); + return null; + } + }; + tests.runInLocale(mRes, Locale.FRENCH); + } + + public void testAdditionalSubtypesInFrenchSystemLocale() { + final RunInLocale<Void> tests = new RunInLocale<Void>() { + @Override + protected Void job(final Resources res) { + assertEquals("fr qwertz", "Français (QWERTZ)", + SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(FR_QWERTZ)); + assertEquals("de qwerty", "Allemand (QWERTY)", + SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(DE_QWERTY)); + assertEquals("en_US azerty", "Anglais (États-Unis) (AZERTY)", + SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(EN_US_AZERTY)); + assertEquals("en_UK dvorak", "Anglais (Royaume-Uni) (Dvorak)", + SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(EN_UK_DVORAK)); + assertEquals("es_US colemak","Espagnol (États-Unis) (Colemak)", + SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(ES_US_COLEMAK)); + assertEquals("zz pc", "Alphabet latin (PC)", + SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(ZZ_PC)); + return null; + } + }; + tests.runInLocale(mRes, Locale.FRENCH); + } + + public void testAllFullDisplayNameForSpacebar() { + for (final InputMethodSubtype subtype : mSubtypesList) { + final String subtypeName = + SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(subtype); + final String spacebarText = SubtypeLocaleUtils.getFullDisplayName(subtype); + final String languageName = + SubtypeLocaleUtils.getSubtypeLocaleDisplayName(subtype.getLocale()); + if (SubtypeLocaleUtils.isNoLanguage(subtype)) { + assertFalse(subtypeName, spacebarText.contains(languageName)); + } else { + assertTrue(subtypeName, spacebarText.contains(languageName)); + } + } + } + + public void testAllMiddleDisplayNameForSpacebar() { + for (final InputMethodSubtype subtype : mSubtypesList) { + final String subtypeName = + SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(subtype); + final String spacebarText = SubtypeLocaleUtils.getMiddleDisplayName(subtype); + if (SubtypeLocaleUtils.isNoLanguage(subtype)) { + assertEquals(subtypeName, + SubtypeLocaleUtils.getKeyboardLayoutSetName(subtype), spacebarText); + } else { + assertEquals(subtypeName, + SubtypeLocaleUtils.getSubtypeLocaleDisplayName(subtype.getLocale()), + spacebarText); + } + } + } + + public void testAllShortDisplayNameForSpacebar() { + for (final InputMethodSubtype subtype : mSubtypesList) { + final String subtypeName = + SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(subtype); + final Locale locale = SubtypeLocaleUtils.getSubtypeLocale(subtype); + final String spacebarText = SubtypeLocaleUtils.getShortDisplayName(subtype); + final String languageCode = StringUtils.capitalizeFirstCodePoint( + locale.getLanguage(), locale); + if (SubtypeLocaleUtils.isNoLanguage(subtype)) { + assertEquals(subtypeName, "", spacebarText); + } else { + assertEquals(subtypeName, languageCode, spacebarText); + } + } + } + + // InputMethodSubtype's display name for spacebar text in its locale. + // isAdditionalSubtype (T=true, F=false) + // locale layout | Short Middle Full + // ------ ------- - ---- --------- ---------------------- + // en_US qwerty F En English English (US) exception + // en_GB qwerty F En English English (UK) exception + // es_US spanish F Es Español Español (EE.UU.) exception + // fr azerty F Fr Français Français + // fr_CA qwerty F Fr Français Français (Canada) + // de qwertz F De Deutsch Deutsch + // zz qwerty F QWERTY QWERTY + // fr qwertz T Fr Français Français + // de qwerty T De Deutsch Deutsch + // en_US azerty T En English English (US) + // zz azerty T AZERTY AZERTY + + private final RunInLocale<Void> testsPredefinedSubtypesForSpacebar = new RunInLocale<Void>() { + @Override + protected Void job(final Resources res) { + assertEquals("en_US", "English (US)", SubtypeLocaleUtils.getFullDisplayName(EN_US)); + assertEquals("en_GB", "English (UK)", SubtypeLocaleUtils.getFullDisplayName(EN_GB)); + assertEquals("es_US", "Español (EE.UU.)", + SubtypeLocaleUtils.getFullDisplayName(ES_US)); + assertEquals("fr ", "Français", SubtypeLocaleUtils.getFullDisplayName(FR)); + assertEquals("fr_CA", "Français (Canada)", + SubtypeLocaleUtils.getFullDisplayName(FR_CA)); + assertEquals("de ", "Deutsch", SubtypeLocaleUtils.getFullDisplayName(DE)); + assertEquals("zz ", "QWERTY", SubtypeLocaleUtils.getFullDisplayName(ZZ)); + + assertEquals("en_US", "English", SubtypeLocaleUtils.getMiddleDisplayName(EN_US)); + assertEquals("en_GB", "English", SubtypeLocaleUtils.getMiddleDisplayName(EN_GB)); + assertEquals("es_US", "Español", SubtypeLocaleUtils.getMiddleDisplayName(ES_US)); + assertEquals("fr ", "Français", SubtypeLocaleUtils.getMiddleDisplayName(FR)); + assertEquals("fr_CA", "Français", SubtypeLocaleUtils.getMiddleDisplayName(FR_CA)); + assertEquals("de ", "Deutsch", SubtypeLocaleUtils.getMiddleDisplayName(DE)); + assertEquals("zz ", "QWERTY", SubtypeLocaleUtils.getMiddleDisplayName(ZZ)); + + assertEquals("en_US", "En", SubtypeLocaleUtils.getShortDisplayName(EN_US)); + assertEquals("en_GB", "En", SubtypeLocaleUtils.getShortDisplayName(EN_GB)); + assertEquals("es_US", "Es", SubtypeLocaleUtils.getShortDisplayName(ES_US)); + assertEquals("fr ", "Fr", SubtypeLocaleUtils.getShortDisplayName(FR)); + assertEquals("fr_CA", "Fr", SubtypeLocaleUtils.getShortDisplayName(FR_CA)); + assertEquals("de ", "De", SubtypeLocaleUtils.getShortDisplayName(DE)); + assertEquals("zz ", "", SubtypeLocaleUtils.getShortDisplayName(ZZ)); + return null; + } + }; + + private final RunInLocale<Void> testsAdditionalSubtypesForSpacebar = new RunInLocale<Void>() { + @Override + protected Void job(final Resources res) { + assertEquals("fr qwertz", "Français", + SubtypeLocaleUtils.getFullDisplayName(FR_QWERTZ)); + assertEquals("de qwerty", "Deutsch", + SubtypeLocaleUtils.getFullDisplayName(DE_QWERTY)); + assertEquals("en_US azerty", "English (US)", + SubtypeLocaleUtils.getFullDisplayName(EN_US_AZERTY)); + assertEquals("zz azerty", "AZERTY", + SubtypeLocaleUtils.getFullDisplayName(ZZ_AZERTY)); + + assertEquals("fr qwertz", "Français", + SubtypeLocaleUtils.getMiddleDisplayName(FR_QWERTZ)); + assertEquals("de qwerty", "Deutsch", + SubtypeLocaleUtils.getMiddleDisplayName(DE_QWERTY)); + assertEquals("en_US azerty", "English", + SubtypeLocaleUtils.getMiddleDisplayName(EN_US_AZERTY)); + assertEquals("zz azerty", "AZERTY", + SubtypeLocaleUtils.getMiddleDisplayName(ZZ_AZERTY)); + + assertEquals("fr qwertz", "Fr", SubtypeLocaleUtils.getShortDisplayName(FR_QWERTZ)); + assertEquals("de qwerty", "De", SubtypeLocaleUtils.getShortDisplayName(DE_QWERTY)); + assertEquals("en_US azerty", "En", + SubtypeLocaleUtils.getShortDisplayName(EN_US_AZERTY)); + assertEquals("zz azerty", "", SubtypeLocaleUtils.getShortDisplayName(ZZ_AZERTY)); + return null; + } + }; + + public void testPredefinedSubtypesForSpacebarInEnglish() { + testsPredefinedSubtypesForSpacebar.runInLocale(mRes, Locale.ENGLISH); + } + + public void testAdditionalSubtypeForSpacebarInEnglish() { + testsAdditionalSubtypesForSpacebar.runInLocale(mRes, Locale.ENGLISH); + } + + public void testPredefinedSubtypesForSpacebarInFrench() { + testsPredefinedSubtypesForSpacebar.runInLocale(mRes, Locale.FRENCH); + } + + public void testAdditionalSubtypeForSpacebarInFrench() { + testsAdditionalSubtypesForSpacebar.runInLocale(mRes, Locale.FRENCH); + } +} diff --git a/tests/src/com/android/inputmethod/latin/UserHistoryDictIOUtilsTests.java b/tests/src/com/android/inputmethod/latin/utils/UserHistoryDictIOUtilsTests.java index 211d012d2..3eabe2b3c 100644 --- a/tests/src/com/android/inputmethod/latin/UserHistoryDictIOUtilsTests.java +++ b/tests/src/com/android/inputmethod/latin/utils/UserHistoryDictIOUtilsTests.java @@ -14,23 +14,26 @@ * limitations under the License. */ -package com.android.inputmethod.latin; +package com.android.inputmethod.latin.utils; import android.content.Context; import android.test.AndroidTestCase; import android.test.suitebuilder.annotation.LargeTest; import android.util.Log; -import com.android.inputmethod.latin.UserHistoryDictIOUtils.BigramDictionaryInterface; -import com.android.inputmethod.latin.UserHistoryDictIOUtils.OnAddWordListener; +import com.android.inputmethod.latin.makedict.DictDecoder; +import com.android.inputmethod.latin.makedict.DictEncoder; import com.android.inputmethod.latin.makedict.FormatSpec; import com.android.inputmethod.latin.makedict.FusionDictionary; -import com.android.inputmethod.latin.makedict.FusionDictionary.CharGroup; +import com.android.inputmethod.latin.makedict.FusionDictionary.PtNode; +import com.android.inputmethod.latin.makedict.Ver3DictDecoder; +import com.android.inputmethod.latin.makedict.Ver3DictEncoder; +import com.android.inputmethod.latin.personalization.UserHistoryDictionaryBigramList; +import com.android.inputmethod.latin.utils.UserHistoryDictIOUtils.BigramDictionaryInterface; +import com.android.inputmethod.latin.utils.UserHistoryDictIOUtils.OnAddWordListener; import java.io.File; -import java.io.FileInputStream; import java.io.FileNotFoundException; -import java.io.FileOutputStream; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; @@ -48,6 +51,7 @@ public class UserHistoryDictIOUtilsTests extends AndroidTestCase private static final int BIGRAM_FREQUENCY = 100; private static final ArrayList<String> NOT_HAVE_BIGRAM = new ArrayList<String>(); private static final FormatSpec.FormatOptions FORMAT_OPTIONS = new FormatSpec.FormatOptions(2); + private static final String TEST_DICT_FILE_EXTENSION = ".testDict"; /** * Return same frequency for all words and bigrams @@ -85,12 +89,12 @@ public class UserHistoryDictIOUtilsTests extends AndroidTestCase private void checkWordInFusionDict(final FusionDictionary dict, final String word, final ArrayList<String> expectedBigrams) { - final CharGroup group = FusionDictionary.findWordInTree(dict.mRoot, word); - assertNotNull(group); - assertTrue(group.isTerminal()); + final PtNode ptNode = FusionDictionary.findWordInTree(dict.mRootNodeArray, word); + assertNotNull(ptNode); + assertTrue(ptNode.isTerminal()); for (final String bigram : expectedBigrams) { - assertNotNull(group.getBigram(bigram)); + assertNotNull(ptNode.getBigram(bigram)); } } @@ -134,39 +138,20 @@ public class UserHistoryDictIOUtilsTests extends AndroidTestCase private void writeDictToFile(final File file, final UserHistoryDictionaryBigramList bigramList) { - try { - final FileOutputStream out = new FileOutputStream(file); - UserHistoryDictIOUtils.writeDictionaryBinary(out, this, bigramList, FORMAT_OPTIONS); - out.flush(); - out.close(); - } catch (IOException e) { - Log.e(TAG, "IO exception while writing file", e); - } + final DictEncoder dictEncoder = new Ver3DictEncoder(file); + UserHistoryDictIOUtils.writeDictionary(dictEncoder, this, bigramList, FORMAT_OPTIONS); } private void readDictFromFile(final File file, final OnAddWordListener listener) { - FileInputStream inStream = null; - + final DictDecoder dictDecoder = FormatSpec.getDictDecoder(file, DictDecoder.USE_BYTEARRAY); try { - inStream = new FileInputStream(file); - final byte[] buffer = new byte[(int)file.length()]; - inStream.read(buffer); - - UserHistoryDictIOUtils.readDictionaryBinary( - new UserHistoryDictIOUtils.ByteArrayWrapper(buffer), listener); + dictDecoder.openDictBuffer(); } catch (FileNotFoundException e) { Log.e(TAG, "file not found", e); } catch (IOException e) { Log.e(TAG, "IOException", e); - } finally { - if (inStream != null) { - try { - inStream.close(); - } catch (IOException e) { - // do nothing - } - } } + UserHistoryDictIOUtils.readDictionaryBinary(dictDecoder, listener); } public void testGenerateFusionDictionary() { @@ -189,7 +174,8 @@ public class UserHistoryDictIOUtilsTests extends AndroidTestCase File file = null; try { - file = File.createTempFile("testReadAndWrite", ".dict", getContext().getCacheDir()); + file = File.createTempFile("testReadAndWrite", TEST_DICT_FILE_EXTENSION, + getContext().getCacheDir()); } catch (IOException e) { Log.d(TAG, "IOException while creating a temporary file", e); } |