diff options
Diffstat (limited to 'tests/src/com/android/inputmethod/latin')
70 files changed, 4271 insertions, 5720 deletions
diff --git a/tests/src/com/android/inputmethod/latin/BinaryDictionaryDecayingTests.java b/tests/src/com/android/inputmethod/latin/BinaryDictionaryDecayingTests.java deleted file mode 100644 index ae184268c..000000000 --- a/tests/src/com/android/inputmethod/latin/BinaryDictionaryDecayingTests.java +++ /dev/null @@ -1,692 +0,0 @@ -/* - * 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.PrevWordsInfo.WordInfo; -import com.android.inputmethod.latin.makedict.BinaryDictIOUtils; -import com.android.inputmethod.latin.makedict.CodePointUtils; -import com.android.inputmethod.latin.makedict.DictDecoder; -import com.android.inputmethod.latin.makedict.DictionaryHeader; -import com.android.inputmethod.latin.makedict.FormatSpec; -import com.android.inputmethod.latin.makedict.FusionDictionary; -import com.android.inputmethod.latin.makedict.FusionDictionary.PtNode; -import com.android.inputmethod.latin.makedict.UnsupportedFormatException; -import com.android.inputmethod.latin.utils.BinaryDictionaryUtils; -import com.android.inputmethod.latin.utils.FileUtils; -import com.android.inputmethod.latin.utils.LocaleUtils; - -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; -import java.util.concurrent.TimeUnit; - -@LargeTest -public class BinaryDictionaryDecayingTests extends AndroidTestCase { - private static final String TEST_DICT_FILE_EXTENSION = ".testDict"; - private static final String TEST_LOCALE = "test"; - private static final int DUMMY_PROBABILITY = 0; - private static final int[] DICT_FORMAT_VERSIONS = - new int[] { FormatSpec.VERSION4, FormatSpec.VERSION4_DEV }; - - private int mCurrentTime = 0; - - @Override - protected void setUp() throws Exception { - super.setUp(); - mCurrentTime = 0; - } - - @Override - protected void tearDown() throws Exception { - stopTestModeInNativeCode(); - super.tearDown(); - } - - private static boolean supportsBeginningOfSentence(final int formatVersion) { - return formatVersion > FormatSpec.VERSION401; - } - - private void addUnigramWord(final BinaryDictionary binaryDictionary, final String word, - final int probability) { - binaryDictionary.addUnigramEntry(word, probability, "" /* shortcutTarget */, - BinaryDictionary.NOT_A_PROBABILITY /* shortcutProbability */, - false /* isBeginningOfSentence */, false /* isNotAWord */, - false /* isBlacklisted */, mCurrentTime /* timestamp */); - } - - private void addBigramWords(final BinaryDictionary binaryDictionary, final String word0, - final String word1, final int probability) { - binaryDictionary.addNgramEntry(new PrevWordsInfo(new WordInfo(word0)), word1, probability, - mCurrentTime /* timestamp */); - } - - private static boolean isValidBigram(final BinaryDictionary binaryDictionary, - final String word0, final String word1) { - return binaryDictionary.isValidNgram(new PrevWordsInfo(new WordInfo(word0)), word1); - } - - private void forcePassingShortTime(final BinaryDictionary binaryDictionary) { - // 30 days. - final int timeToElapse = (int)TimeUnit.SECONDS.convert(30, TimeUnit.DAYS); - mCurrentTime += timeToElapse; - setCurrentTimeForTestMode(mCurrentTime); - binaryDictionary.flushWithGC(); - } - - private void forcePassingLongTime(final BinaryDictionary binaryDictionary) { - // 365 days. - final int timeToElapse = (int)TimeUnit.SECONDS.convert(365, TimeUnit.DAYS); - mCurrentTime += timeToElapse; - setCurrentTimeForTestMode(mCurrentTime); - binaryDictionary.flushWithGC(); - } - - private File createEmptyDictionaryAndGetFile(final String dictId, - final int formatVersion) throws IOException { - if (formatVersion == FormatSpec.VERSION4 - || formatVersion == FormatSpec.VERSION4_ONLY_FOR_TESTING - || formatVersion == FormatSpec.VERSION4_DEV) { - return createEmptyVer4DictionaryAndGetFile(dictId, formatVersion); - } else { - throw new IOException("Dictionary format version " + formatVersion - + " is not supported."); - } - } - - private File createEmptyVer4DictionaryAndGetFile(final String dictId, final int formatVersion) - throws IOException { - final File file = File.createTempFile(dictId, TEST_DICT_FILE_EXTENSION, - getContext().getCacheDir()); - FileUtils.deleteRecursively(file); - Map<String, String> attributeMap = new HashMap<>(); - attributeMap.put(DictionaryHeader.DICTIONARY_ID_KEY, dictId); - attributeMap.put(DictionaryHeader.DICTIONARY_VERSION_KEY, - String.valueOf(TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis()))); - attributeMap.put(DictionaryHeader.USES_FORGETTING_CURVE_KEY, - DictionaryHeader.ATTRIBUTE_VALUE_TRUE); - attributeMap.put(DictionaryHeader.HAS_HISTORICAL_INFO_KEY, - DictionaryHeader.ATTRIBUTE_VALUE_TRUE); - if (BinaryDictionaryUtils.createEmptyDictFile(file.getAbsolutePath(), formatVersion, - LocaleUtils.constructLocaleFromString(TEST_LOCALE), attributeMap)) { - return file; - } else { - throw new IOException("Empty dictionary " + file.getAbsolutePath() - + " cannot be created. Foramt version: " + formatVersion); - } - } - - private static int setCurrentTimeForTestMode(final int currentTime) { - return BinaryDictionaryUtils.setCurrentTimeForTest(currentTime); - } - - private static int stopTestModeInNativeCode() { - return BinaryDictionaryUtils.setCurrentTimeForTest(-1); - } - - public void testReadDictInJavaSide() { - for (final int formatVersion : DICT_FORMAT_VERSIONS) { - testReadDictInJavaSide(formatVersion); - } - } - - private void testReadDictInJavaSide(final int formatVersion) { - setCurrentTimeForTestMode(mCurrentTime); - File dictFile = null; - try { - dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary", formatVersion); - } 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 */); - addUnigramWord(binaryDictionary, "a", DUMMY_PROBABILITY); - addUnigramWord(binaryDictionary, "ab", DUMMY_PROBABILITY); - addUnigramWord(binaryDictionary, "aaa", DUMMY_PROBABILITY); - addBigramWords(binaryDictionary, "a", "aaa", DUMMY_PROBABILITY); - binaryDictionary.flushWithGC(); - binaryDictionary.close(); - - final DictDecoder dictDecoder = - BinaryDictIOUtils.getDictDecoder(dictFile, 0, dictFile.length()); - try { - final FusionDictionary dict = - dictDecoder.readDictionaryBinary(false /* deleteDictIfBroken */); - PtNode ptNode = FusionDictionary.findWordInTree(dict.mRootNodeArray, "a"); - assertNotNull(ptNode); - assertTrue(ptNode.isTerminal()); - assertNotNull(ptNode.getBigram("aaa")); - ptNode = FusionDictionary.findWordInTree(dict.mRootNodeArray, "ab"); - assertNotNull(ptNode); - assertTrue(ptNode.isTerminal()); - ptNode = FusionDictionary.findWordInTree(dict.mRootNodeArray, "aaa"); - assertNotNull(ptNode); - assertTrue(ptNode.isTerminal()); - } catch (IOException e) { - fail("IOException while reading dictionary: " + e); - } catch (UnsupportedFormatException e) { - fail("Unsupported format: " + e); - } - dictFile.delete(); - } - - public void testControlCurrentTime() { - final int TEST_COUNT = 1000; - final long seed = System.currentTimeMillis(); - final Random random = new Random(seed); - final int startTime = stopTestModeInNativeCode(); - for (int i = 0; i < TEST_COUNT; i++) { - final int currentTime = random.nextInt(Integer.MAX_VALUE); - final int currentTimeInNativeCode = setCurrentTimeForTestMode(currentTime); - assertEquals(currentTime, currentTimeInNativeCode); - } - final int endTime = stopTestModeInNativeCode(); - final int MAX_ALLOWED_ELAPSED_TIME = 10; - assertTrue(startTime <= endTime && endTime <= startTime + MAX_ALLOWED_ELAPSED_TIME); - } - - public void testAddValidAndInvalidWords() { - for (final int formatVersion : DICT_FORMAT_VERSIONS) { - testAddValidAndInvalidWords(formatVersion); - } - } - - private void testAddValidAndInvalidWords(final int formatVersion) { - File dictFile = null; - try { - dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary", formatVersion); - } 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 */); - - addUnigramWord(binaryDictionary, "a", Dictionary.NOT_A_PROBABILITY); - assertFalse(binaryDictionary.isValidWord("a")); - addUnigramWord(binaryDictionary, "a", Dictionary.NOT_A_PROBABILITY); - addUnigramWord(binaryDictionary, "a", Dictionary.NOT_A_PROBABILITY); - assertTrue(binaryDictionary.isValidWord("a")); - - addUnigramWord(binaryDictionary, "b", DUMMY_PROBABILITY); - assertTrue(binaryDictionary.isValidWord("b")); - - addBigramWords(binaryDictionary, "a", "b", Dictionary.NOT_A_PROBABILITY); - assertFalse(isValidBigram(binaryDictionary, "a", "b")); - addBigramWords(binaryDictionary, "a", "b", Dictionary.NOT_A_PROBABILITY); - assertTrue(isValidBigram(binaryDictionary, "a", "b")); - - addUnigramWord(binaryDictionary, "c", DUMMY_PROBABILITY); - addBigramWords(binaryDictionary, "a", "c", DUMMY_PROBABILITY); - assertTrue(isValidBigram(binaryDictionary, "a", "c")); - - // Add bigrams of not valid unigrams. - addBigramWords(binaryDictionary, "x", "y", Dictionary.NOT_A_PROBABILITY); - assertFalse(isValidBigram(binaryDictionary, "x", "y")); - addBigramWords(binaryDictionary, "x", "y", DUMMY_PROBABILITY); - assertFalse(isValidBigram(binaryDictionary, "x", "y")); - - binaryDictionary.close(); - dictFile.delete(); - } - - public void testDecayingProbability() { - for (final int formatVersion : DICT_FORMAT_VERSIONS) { - testDecayingProbability(formatVersion); - } - } - - private void testDecayingProbability(final int formatVersion) { - File dictFile = null; - try { - dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary", formatVersion); - } 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 */); - - addUnigramWord(binaryDictionary, "a", DUMMY_PROBABILITY); - assertTrue(binaryDictionary.isValidWord("a")); - forcePassingShortTime(binaryDictionary); - assertFalse(binaryDictionary.isValidWord("a")); - - addUnigramWord(binaryDictionary, "a", DUMMY_PROBABILITY); - addUnigramWord(binaryDictionary, "a", DUMMY_PROBABILITY); - addUnigramWord(binaryDictionary, "a", DUMMY_PROBABILITY); - assertTrue(binaryDictionary.isValidWord("a")); - forcePassingShortTime(binaryDictionary); - assertTrue(binaryDictionary.isValidWord("a")); - forcePassingLongTime(binaryDictionary); - assertFalse(binaryDictionary.isValidWord("a")); - - addUnigramWord(binaryDictionary, "a", DUMMY_PROBABILITY); - addUnigramWord(binaryDictionary, "b", DUMMY_PROBABILITY); - addBigramWords(binaryDictionary, "a", "b", DUMMY_PROBABILITY); - assertTrue(isValidBigram(binaryDictionary, "a", "b")); - forcePassingShortTime(binaryDictionary); - assertFalse(isValidBigram(binaryDictionary, "a", "b")); - - addUnigramWord(binaryDictionary, "a", DUMMY_PROBABILITY); - addUnigramWord(binaryDictionary, "b", DUMMY_PROBABILITY); - addBigramWords(binaryDictionary, "a", "b", DUMMY_PROBABILITY); - addUnigramWord(binaryDictionary, "a", DUMMY_PROBABILITY); - addUnigramWord(binaryDictionary, "b", DUMMY_PROBABILITY); - addBigramWords(binaryDictionary, "a", "b", DUMMY_PROBABILITY); - addUnigramWord(binaryDictionary, "a", DUMMY_PROBABILITY); - addUnigramWord(binaryDictionary, "b", DUMMY_PROBABILITY); - addBigramWords(binaryDictionary, "a", "b", DUMMY_PROBABILITY); - assertTrue(isValidBigram(binaryDictionary, "a", "b")); - forcePassingShortTime(binaryDictionary); - assertTrue(isValidBigram(binaryDictionary, "a", "b")); - forcePassingLongTime(binaryDictionary); - assertFalse(isValidBigram(binaryDictionary, "a", "b")); - - binaryDictionary.close(); - dictFile.delete(); - } - - public void testAddManyUnigramsToDecayingDict() { - for (final int formatVersion : DICT_FORMAT_VERSIONS) { - testAddManyUnigramsToDecayingDict(formatVersion); - } - } - - private void testAddManyUnigramsToDecayingDict(final int formatVersion) { - final int unigramCount = 30000; - final int unigramTypedCount = 100000; - final int codePointSetSize = 50; - final long seed = System.currentTimeMillis(); - final Random random = new Random(seed); - - File dictFile = null; - try { - dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary", formatVersion); - } 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 */); - setCurrentTimeForTestMode(mCurrentTime); - - final int[] codePointSet = CodePointUtils.generateCodePointSet(codePointSetSize, random); - final ArrayList<String> words = new ArrayList<>(); - - for (int i = 0; i < unigramCount; i++) { - final String word = CodePointUtils.generateWord(random, codePointSet); - words.add(word); - } - - final int maxUnigramCount = Integer.parseInt( - binaryDictionary.getPropertyForTest(BinaryDictionary.MAX_UNIGRAM_COUNT_QUERY)); - for (int i = 0; i < unigramTypedCount; i++) { - final String word = words.get(random.nextInt(words.size())); - addUnigramWord(binaryDictionary, word, DUMMY_PROBABILITY); - - if (binaryDictionary.needsToRunGC(true /* mindsBlockByGC */)) { - final int unigramCountBeforeGC = - Integer.parseInt(binaryDictionary.getPropertyForTest( - BinaryDictionary.UNIGRAM_COUNT_QUERY)); - while (binaryDictionary.needsToRunGC(true /* mindsBlockByGC */)) { - forcePassingShortTime(binaryDictionary); - } - final int unigramCountAfterGC = - Integer.parseInt(binaryDictionary.getPropertyForTest( - BinaryDictionary.UNIGRAM_COUNT_QUERY)); - assertTrue(unigramCountBeforeGC > unigramCountAfterGC); - } - } - - assertTrue(Integer.parseInt(binaryDictionary.getPropertyForTest( - BinaryDictionary.UNIGRAM_COUNT_QUERY)) > 0); - assertTrue(Integer.parseInt(binaryDictionary.getPropertyForTest( - BinaryDictionary.UNIGRAM_COUNT_QUERY)) <= maxUnigramCount); - forcePassingLongTime(binaryDictionary); - assertEquals(0, Integer.parseInt(binaryDictionary.getPropertyForTest( - BinaryDictionary.UNIGRAM_COUNT_QUERY))); - } - - public void testOverflowUnigrams() { - for (final int formatVersion : DICT_FORMAT_VERSIONS) { - testOverflowUnigrams(formatVersion); - } - } - - private void testOverflowUnigrams(final int formatVersion) { - final int unigramCount = 20000; - final int eachUnigramTypedCount = 2; - final int strongUnigramTypedCount = 20; - final int weakUnigramTypedCount = 1; - final int codePointSetSize = 50; - final long seed = System.currentTimeMillis(); - final Random random = new Random(seed); - - File dictFile = null; - try { - dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary", formatVersion); - } 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 */); - setCurrentTimeForTestMode(mCurrentTime); - final int[] codePointSet = CodePointUtils.generateCodePointSet(codePointSetSize, random); - - final String strong = "strong"; - final String weak = "weak"; - for (int j = 0; j < strongUnigramTypedCount; j++) { - addUnigramWord(binaryDictionary, strong, DUMMY_PROBABILITY); - } - for (int j = 0; j < weakUnigramTypedCount; j++) { - addUnigramWord(binaryDictionary, weak, DUMMY_PROBABILITY); - } - assertTrue(binaryDictionary.isValidWord(strong)); - assertTrue(binaryDictionary.isValidWord(weak)); - - for (int i = 0; i < unigramCount; i++) { - final String word = CodePointUtils.generateWord(random, codePointSet); - for (int j = 0; j < eachUnigramTypedCount; j++) { - addUnigramWord(binaryDictionary, word, DUMMY_PROBABILITY); - } - if (binaryDictionary.needsToRunGC(true /* mindsBlockByGC */)) { - final int unigramCountBeforeGC = - Integer.parseInt(binaryDictionary.getPropertyForTest( - BinaryDictionary.UNIGRAM_COUNT_QUERY)); - assertTrue(binaryDictionary.isValidWord(strong)); - assertTrue(binaryDictionary.isValidWord(weak)); - binaryDictionary.flushWithGC(); - final int unigramCountAfterGC = - Integer.parseInt(binaryDictionary.getPropertyForTest( - BinaryDictionary.UNIGRAM_COUNT_QUERY)); - assertTrue(unigramCountBeforeGC > unigramCountAfterGC); - assertFalse(binaryDictionary.isValidWord(weak)); - assertTrue(binaryDictionary.isValidWord(strong)); - break; - } - } - } - - public void testAddManyBigramsToDecayingDict() { - for (final int formatVersion : DICT_FORMAT_VERSIONS) { - testAddManyBigramsToDecayingDict(formatVersion); - } - } - - private void testAddManyBigramsToDecayingDict(final int formatVersion) { - final int unigramCount = 5000; - final int bigramCount = 30000; - final int bigramTypedCount = 100000; - final int codePointSetSize = 50; - final long seed = System.currentTimeMillis(); - final Random random = new Random(seed); - - File dictFile = null; - try { - dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary", formatVersion); - } 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 */); - setCurrentTimeForTestMode(mCurrentTime); - - final int[] codePointSet = CodePointUtils.generateCodePointSet(codePointSetSize, random); - final ArrayList<String> words = new ArrayList<>(); - final ArrayList<Pair<String, String>> bigrams = new ArrayList<>(); - - for (int i = 0; i < unigramCount; ++i) { - final String word = CodePointUtils.generateWord(random, codePointSet); - words.add(word); - } - for (int i = 0; i < bigramCount; ++i) { - final int word0Index = random.nextInt(words.size()); - int word1Index = random.nextInt(words.size() - 1); - if (word1Index >= word0Index) { - word1Index += 1; - } - final String word0 = words.get(word0Index); - final String word1 = words.get(word1Index); - final Pair<String, String> bigram = new Pair<>(word0, word1); - bigrams.add(bigram); - } - - final int maxBigramCount = Integer.parseInt( - binaryDictionary.getPropertyForTest(BinaryDictionary.MAX_BIGRAM_COUNT_QUERY)); - for (int i = 0; i < bigramTypedCount; ++i) { - final Pair<String, String> bigram = bigrams.get(random.nextInt(bigrams.size())); - addUnigramWord(binaryDictionary, bigram.first, DUMMY_PROBABILITY); - addUnigramWord(binaryDictionary, bigram.second, DUMMY_PROBABILITY); - addBigramWords(binaryDictionary, bigram.first, bigram.second, DUMMY_PROBABILITY); - - if (binaryDictionary.needsToRunGC(true /* mindsBlockByGC */)) { - final int bigramCountBeforeGC = - Integer.parseInt(binaryDictionary.getPropertyForTest( - BinaryDictionary.BIGRAM_COUNT_QUERY)); - while (binaryDictionary.needsToRunGC(true /* mindsBlockByGC */)) { - forcePassingShortTime(binaryDictionary); - } - final int bigramCountAfterGC = - Integer.parseInt(binaryDictionary.getPropertyForTest( - BinaryDictionary.BIGRAM_COUNT_QUERY)); - assertTrue(bigramCountBeforeGC > bigramCountAfterGC); - } - } - - assertTrue(Integer.parseInt(binaryDictionary.getPropertyForTest( - BinaryDictionary.BIGRAM_COUNT_QUERY)) > 0); - assertTrue(Integer.parseInt(binaryDictionary.getPropertyForTest( - BinaryDictionary.BIGRAM_COUNT_QUERY)) <= maxBigramCount); - forcePassingLongTime(binaryDictionary); - assertEquals(0, Integer.parseInt(binaryDictionary.getPropertyForTest( - BinaryDictionary.BIGRAM_COUNT_QUERY))); - } - - public void testOverflowBigrams() { - for (final int formatVersion : DICT_FORMAT_VERSIONS) { - testOverflowBigrams(formatVersion); - } - } - - private void testOverflowBigrams(final int formatVersion) { - final int bigramCount = 20000; - final int unigramCount = 1000; - final int unigramTypedCount = 20; - final int eachBigramTypedCount = 2; - final int strongBigramTypedCount = 20; - final int weakBigramTypedCount = 1; - final int codePointSetSize = 50; - final long seed = System.currentTimeMillis(); - final Random random = new Random(seed); - - File dictFile = null; - try { - dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary", formatVersion); - } 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 */); - setCurrentTimeForTestMode(mCurrentTime); - final int[] codePointSet = CodePointUtils.generateCodePointSet(codePointSetSize, random); - - final ArrayList<String> words = new ArrayList<>(); - for (int i = 0; i < unigramCount; i++) { - final String word = CodePointUtils.generateWord(random, codePointSet); - words.add(word); - for (int j = 0; j < unigramTypedCount; j++) { - addUnigramWord(binaryDictionary, word, DUMMY_PROBABILITY); - } - } - final String strong = "strong"; - final String weak = "weak"; - final String target = "target"; - for (int j = 0; j < unigramTypedCount; j++) { - addUnigramWord(binaryDictionary, strong, DUMMY_PROBABILITY); - addUnigramWord(binaryDictionary, weak, DUMMY_PROBABILITY); - addUnigramWord(binaryDictionary, target, DUMMY_PROBABILITY); - } - binaryDictionary.flushWithGC(); - for (int j = 0; j < strongBigramTypedCount; j++) { - addBigramWords(binaryDictionary, strong, target, DUMMY_PROBABILITY); - } - for (int j = 0; j < weakBigramTypedCount; j++) { - addBigramWords(binaryDictionary, weak, target, DUMMY_PROBABILITY); - } - assertTrue(isValidBigram(binaryDictionary, strong, target)); - assertTrue(isValidBigram(binaryDictionary, weak, target)); - - for (int i = 0; i < bigramCount; i++) { - final int word0Index = random.nextInt(words.size()); - final String word0 = words.get(word0Index); - final int index = random.nextInt(words.size() - 1); - final int word1Index = (index >= word0Index) ? index + 1 : index; - final String word1 = words.get(word1Index); - - for (int j = 0; j < eachBigramTypedCount; j++) { - addBigramWords(binaryDictionary, word0, word1, DUMMY_PROBABILITY); - } - if (binaryDictionary.needsToRunGC(true /* mindsBlockByGC */)) { - final int bigramCountBeforeGC = - Integer.parseInt(binaryDictionary.getPropertyForTest( - BinaryDictionary.BIGRAM_COUNT_QUERY)); - binaryDictionary.flushWithGC(); - final int bigramCountAfterGC = - Integer.parseInt(binaryDictionary.getPropertyForTest( - BinaryDictionary.BIGRAM_COUNT_QUERY)); - assertTrue(bigramCountBeforeGC > bigramCountAfterGC); - assertTrue(isValidBigram(binaryDictionary, strong, target)); - assertFalse(isValidBigram(binaryDictionary, weak, target)); - break; - } - } - } - - public void testDictMigration() { - for (final int formatVersion : DICT_FORMAT_VERSIONS) { - testDictMigration(FormatSpec.VERSION4_ONLY_FOR_TESTING, formatVersion); - } - } - - private void testDictMigration(final int fromFormatVersion, final int toFormatVersion) { - setCurrentTimeForTestMode(mCurrentTime); - File dictFile = null; - try { - dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary", fromFormatVersion); - } catch (IOException e) { - fail("IOException while writing an initial dictionary : " + e); - } - final BinaryDictionary binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(), - 0 /* offset */, dictFile.length(), true /* useFullEditDistance */, - Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */); - addUnigramWord(binaryDictionary, "aaa", DUMMY_PROBABILITY); - assertTrue(binaryDictionary.isValidWord("aaa")); - addUnigramWord(binaryDictionary, "bbb", Dictionary.NOT_A_PROBABILITY); - assertFalse(binaryDictionary.isValidWord("bbb")); - addUnigramWord(binaryDictionary, "ccc", DUMMY_PROBABILITY); - addUnigramWord(binaryDictionary, "ccc", DUMMY_PROBABILITY); - addUnigramWord(binaryDictionary, "ccc", DUMMY_PROBABILITY); - addUnigramWord(binaryDictionary, "ccc", DUMMY_PROBABILITY); - addUnigramWord(binaryDictionary, "ccc", DUMMY_PROBABILITY); - addUnigramWord(binaryDictionary, "abc", DUMMY_PROBABILITY); - addBigramWords(binaryDictionary, "aaa", "abc", DUMMY_PROBABILITY); - assertTrue(isValidBigram(binaryDictionary, "aaa", "abc")); - addBigramWords(binaryDictionary, "aaa", "bbb", Dictionary.NOT_A_PROBABILITY); - assertFalse(isValidBigram(binaryDictionary, "aaa", "bbb")); - - assertEquals(fromFormatVersion, binaryDictionary.getFormatVersion()); - assertTrue(binaryDictionary.migrateTo(toFormatVersion)); - assertTrue(binaryDictionary.isValidDictionary()); - assertEquals(toFormatVersion, binaryDictionary.getFormatVersion()); - assertTrue(binaryDictionary.isValidWord("aaa")); - assertFalse(binaryDictionary.isValidWord("bbb")); - assertTrue(binaryDictionary.getFrequency("aaa") < binaryDictionary.getFrequency("ccc")); - addUnigramWord(binaryDictionary, "bbb", Dictionary.NOT_A_PROBABILITY); - assertTrue(binaryDictionary.isValidWord("bbb")); - assertTrue(isValidBigram(binaryDictionary, "aaa", "abc")); - assertFalse(isValidBigram(binaryDictionary, "aaa", "bbb")); - addBigramWords(binaryDictionary, "aaa", "bbb", Dictionary.NOT_A_PROBABILITY); - assertTrue(isValidBigram(binaryDictionary, "aaa", "bbb")); - binaryDictionary.close(); - dictFile.delete(); - } - - public void testBeginningOfSentence() { - for (final int formatVersion : DICT_FORMAT_VERSIONS) { - if (supportsBeginningOfSentence(formatVersion)) { - testBeginningOfSentence(formatVersion); - } - } - } - - private void testBeginningOfSentence(final int formatVersion) { - setCurrentTimeForTestMode(mCurrentTime); - File dictFile = null; - try { - dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary", formatVersion); - } catch (IOException e) { - fail("IOException while writing an initial dictionary : " + e); - } - final BinaryDictionary binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(), - 0 /* offset */, dictFile.length(), true /* useFullEditDistance */, - Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */); - - binaryDictionary.addUnigramEntry("", DUMMY_PROBABILITY, "" /* shortcutTarget */, - BinaryDictionary.NOT_A_PROBABILITY /* shortcutProbability */, - true /* isBeginningOfSentence */, true /* isNotAWord */, false /* isBlacklisted */, - mCurrentTime); - final PrevWordsInfo prevWordsInfoStartOfSentence = PrevWordsInfo.BEGINNING_OF_SENTENCE; - addUnigramWord(binaryDictionary, "aaa", DUMMY_PROBABILITY); - binaryDictionary.addNgramEntry(prevWordsInfoStartOfSentence, "aaa", DUMMY_PROBABILITY, - mCurrentTime); - assertTrue(binaryDictionary.isValidNgram(prevWordsInfoStartOfSentence, "aaa")); - binaryDictionary.addNgramEntry(prevWordsInfoStartOfSentence, "aaa", DUMMY_PROBABILITY, - mCurrentTime); - addUnigramWord(binaryDictionary, "bbb", DUMMY_PROBABILITY); - binaryDictionary.addNgramEntry(prevWordsInfoStartOfSentence, "bbb", DUMMY_PROBABILITY, - mCurrentTime); - assertTrue(binaryDictionary.isValidNgram(prevWordsInfoStartOfSentence, "aaa")); - assertTrue(binaryDictionary.isValidNgram(prevWordsInfoStartOfSentence, "bbb")); - - forcePassingLongTime(binaryDictionary); - assertFalse(binaryDictionary.isValidNgram(prevWordsInfoStartOfSentence, "aaa")); - assertFalse(binaryDictionary.isValidNgram(prevWordsInfoStartOfSentence, "bbb")); - - addUnigramWord(binaryDictionary, "aaa", DUMMY_PROBABILITY); - binaryDictionary.addNgramEntry(prevWordsInfoStartOfSentence, "aaa", DUMMY_PROBABILITY, - mCurrentTime); - addUnigramWord(binaryDictionary, "bbb", DUMMY_PROBABILITY); - binaryDictionary.addNgramEntry(prevWordsInfoStartOfSentence, "bbb", DUMMY_PROBABILITY, - mCurrentTime); - assertTrue(binaryDictionary.isValidNgram(prevWordsInfoStartOfSentence, "aaa")); - assertTrue(binaryDictionary.isValidNgram(prevWordsInfoStartOfSentence, "bbb")); - binaryDictionary.close(); - dictFile.delete(); - } -} diff --git a/tests/src/com/android/inputmethod/latin/BinaryDictionaryTests.java b/tests/src/com/android/inputmethod/latin/BinaryDictionaryTests.java index 6ba18d665..e96c934cb 100644 --- a/tests/src/com/android/inputmethod/latin/BinaryDictionaryTests.java +++ b/tests/src/com/android/inputmethod/latin/BinaryDictionaryTests.java @@ -21,14 +21,14 @@ import android.test.suitebuilder.annotation.LargeTest; import android.text.TextUtils; import android.util.Pair; -import com.android.inputmethod.latin.PrevWordsInfo.WordInfo; -import com.android.inputmethod.latin.makedict.CodePointUtils; +import com.android.inputmethod.latin.NgramContext.WordInfo; +import com.android.inputmethod.latin.common.CodePointUtils; +import com.android.inputmethod.latin.common.FileUtils; +import com.android.inputmethod.latin.makedict.DictionaryHeader; import com.android.inputmethod.latin.makedict.FormatSpec; import com.android.inputmethod.latin.makedict.WeightedString; import com.android.inputmethod.latin.makedict.WordProperty; import com.android.inputmethod.latin.utils.BinaryDictionaryUtils; -import com.android.inputmethod.latin.utils.FileUtils; -import com.android.inputmethod.latin.utils.LanguageModelParam; import java.io.File; import java.io.IOException; @@ -36,103 +36,100 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.Locale; -import java.util.Map; import java.util.Random; -// TODO Use the seed passed as an argument for makedict test. @LargeTest public class BinaryDictionaryTests extends AndroidTestCase { private static final String TEST_DICT_FILE_EXTENSION = ".testDict"; private static final String TEST_LOCALE = "test"; - private static final int[] DICT_FORMAT_VERSIONS = - new int[] { FormatSpec.VERSION4, FormatSpec.VERSION4_DEV }; + private static final String DICTIONARY_ID = "TestBinaryDictionary"; - private static boolean canCheckBigramProbability(final int formatVersion) { - return formatVersion > FormatSpec.VERSION401; + private HashSet<File> mDictFilesToBeDeleted = new HashSet<>(); + + @Override + protected void setUp() throws Exception { + super.setUp(); + mDictFilesToBeDeleted.clear(); + } + + @Override + protected void tearDown() throws Exception { + for (final File dictFile : mDictFilesToBeDeleted) { + dictFile.delete(); + } + mDictFilesToBeDeleted.clear(); + super.tearDown(); } - private static boolean supportsBeginningOfSentence(final int formatVersion) { - return formatVersion > FormatSpec.VERSION401; + private File createEmptyDictionaryAndGetFile(final int formatVersion) { + return createEmptyDictionaryWithAttributesAndGetFile(formatVersion, + new HashMap<String, String>()); } - private File createEmptyDictionaryAndGetFile(final String dictId, - final int formatVersion) throws IOException { - if (formatVersion == FormatSpec.VERSION4 - || formatVersion == FormatSpec.VERSION4_ONLY_FOR_TESTING - || formatVersion == FormatSpec.VERSION4_DEV) { - return createEmptyVer4DictionaryAndGetFile(dictId, formatVersion); - } else { - throw new IOException("Dictionary format version " + formatVersion - + " is not supported."); + private File createEmptyDictionaryWithAttributesAndGetFile(final int formatVersion, + final HashMap<String, String> attributeMap) { + try { + final File dictFile = createEmptyVer4DictionaryAndGetFile(formatVersion, + attributeMap); + mDictFilesToBeDeleted.add(dictFile); + return dictFile; + } catch (final IOException e) { + fail(e.toString()); } + return null; } - private File createEmptyVer4DictionaryAndGetFile(final String dictId, - final int formatVersion) throws IOException { - final File file = File.createTempFile(dictId, TEST_DICT_FILE_EXTENSION, + private File createEmptyVer4DictionaryAndGetFile(final int formatVersion, + final HashMap<String, String> attributeMap) throws IOException { + final File file = File.createTempFile(DICTIONARY_ID, TEST_DICT_FILE_EXTENSION, getContext().getCacheDir()); file.delete(); file.mkdir(); - Map<String, String> attributeMap = new HashMap<>(); if (BinaryDictionaryUtils.createEmptyDictFile(file.getAbsolutePath(), formatVersion, Locale.ENGLISH, attributeMap)) { return file; - } else { - throw new IOException("Empty dictionary " + file.getAbsolutePath() - + " cannot be created. Format version: " + formatVersion); } + throw new IOException("Empty dictionary " + file.getAbsolutePath() + + " cannot be created. Format version: " + formatVersion); } - public void testIsValidDictionary() { - for (final int formatVersion : DICT_FORMAT_VERSIONS) { - testIsValidDictionary(formatVersion); - } + private static BinaryDictionary getBinaryDictionary(final File dictFile) { + return new BinaryDictionary(dictFile.getAbsolutePath(), + 0 /* offset */, dictFile.length(), true /* useFullEditDistance */, + Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */); } - private void testIsValidDictionary(final int formatVersion) { - File dictFile = null; - try { - dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary", formatVersion); - } catch (IOException e) { - fail("IOException while writing an initial dictionary : " + e); - } - BinaryDictionary binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(), + private BinaryDictionary getEmptyBinaryDictionary(final int formatVersion) { + final File dictFile = createEmptyDictionaryAndGetFile(formatVersion); + return new BinaryDictionary(dictFile.getAbsolutePath(), 0 /* offset */, dictFile.length(), true /* useFullEditDistance */, Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */); + } + + public void testIsValidDictionary() { + final File dictFile = createEmptyDictionaryAndGetFile(FormatSpec.VERSION403); + BinaryDictionary binaryDictionary = getBinaryDictionary(dictFile); assertTrue("binaryDictionary must be valid for existing valid dictionary file.", binaryDictionary.isValidDictionary()); binaryDictionary.close(); assertFalse("binaryDictionary must be invalid after closing.", binaryDictionary.isValidDictionary()); FileUtils.deleteRecursively(dictFile); - binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(), 0 /* offset */, - dictFile.length(), true /* useFullEditDistance */, Locale.getDefault(), - TEST_LOCALE, true /* isUpdatable */); + binaryDictionary = getBinaryDictionary(dictFile); assertFalse("binaryDictionary must be invalid for not existing dictionary file.", binaryDictionary.isValidDictionary()); binaryDictionary.close(); } public void testConstructingDictionaryOnMemory() { - for (final int formatVersion : DICT_FORMAT_VERSIONS) { - testConstructingDictionaryOnMemory(formatVersion); - } - } - - private void testConstructingDictionaryOnMemory(final int formatVersion) { - File dictFile = null; - try { - dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary", formatVersion); - } catch (IOException e) { - fail("IOException while writing an initial dictionary : " + e); - } + final File dictFile = createEmptyDictionaryAndGetFile(FormatSpec.VERSION403); FileUtils.deleteRecursively(dictFile); assertFalse(dictFile.exists()); - BinaryDictionary binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(), - true /* useFullEditDistance */, Locale.getDefault(), TEST_LOCALE, formatVersion, - new HashMap<String, String>()); + final BinaryDictionary binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(), + true /* useFullEditDistance */, Locale.getDefault(), TEST_LOCALE, + FormatSpec.VERSION403, new HashMap<String, String>()); assertTrue(binaryDictionary.isValidDictionary()); - assertEquals(formatVersion, binaryDictionary.getFormatVersion()); + assertEquals(FormatSpec.VERSION403, binaryDictionary.getFormatVersion()); final int probability = 100; addUnigramWord(binaryDictionary, "word", probability); assertEquals(probability, binaryDictionary.getFrequency("word")); @@ -140,31 +137,15 @@ public class BinaryDictionaryTests extends AndroidTestCase { binaryDictionary.flush(); assertTrue(dictFile.exists()); assertTrue(binaryDictionary.isValidDictionary()); - assertEquals(formatVersion, binaryDictionary.getFormatVersion()); + assertEquals(FormatSpec.VERSION403, binaryDictionary.getFormatVersion()); assertEquals(probability, binaryDictionary.getFrequency("word")); binaryDictionary.close(); - dictFile.delete(); } public void testAddTooLongWord() { - for (final int formatVersion : DICT_FORMAT_VERSIONS) { - testAddTooLongWord(formatVersion); - } - } - - private void testAddTooLongWord(final int formatVersion) { - File dictFile = null; - try { - dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary", formatVersion); - } catch (IOException e) { - fail("IOException while writing an initial dictionary : " + e); - } - final BinaryDictionary binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(), - 0 /* offset */, dictFile.length(), true /* useFullEditDistance */, - Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */); - + final BinaryDictionary binaryDictionary = getEmptyBinaryDictionary(FormatSpec.VERSION403); final StringBuffer stringBuilder = new StringBuffer(); - for (int i = 0; i < Constants.DICTIONARY_MAX_WORD_LENGTH; i++) { + for (int i = 0; i < BinaryDictionary.DICTIONARY_MAX_WORD_LENGTH; i++) { stringBuilder.append('a'); } final String validLongWord = stringBuilder.toString(); @@ -175,9 +156,8 @@ public class BinaryDictionaryTests extends AndroidTestCase { addUnigramWord(binaryDictionary, validLongWord, probability); addUnigramWord(binaryDictionary, invalidLongWord, probability); // Too long short cut. - binaryDictionary.addUnigramEntry("a", probability, invalidLongWord, - 10 /* shortcutProbability */, false /* isBeginningOfSentence */, - false /* isNotAWord */, false /* isBlacklisted */, + binaryDictionary.addUnigramEntry("a", probability, false /* isBeginningOfSentence */, + false /* isNotAWord */, false /* isPossiblyOffensive */, BinaryDictionary.NOT_A_VALID_TIMESTAMP); addUnigramWord(binaryDictionary, "abc", probability); final int updatedProbability = 200; @@ -188,58 +168,49 @@ public class BinaryDictionaryTests extends AndroidTestCase { assertEquals(probability, binaryDictionary.getFrequency("aaa")); assertEquals(updatedProbability, binaryDictionary.getFrequency(validLongWord)); - assertEquals(BinaryDictionary.NOT_A_PROBABILITY, - binaryDictionary.getFrequency(invalidLongWord)); + assertEquals(Dictionary.NOT_A_PROBABILITY, binaryDictionary.getFrequency(invalidLongWord)); assertEquals(updatedProbability, binaryDictionary.getFrequency("abc")); - dictFile.delete(); } private static void addUnigramWord(final BinaryDictionary binaryDictionary, final String word, final int probability) { - binaryDictionary.addUnigramEntry(word, probability, "" /* shortcutTarget */, - BinaryDictionary.NOT_A_PROBABILITY /* shortcutProbability */, + binaryDictionary.addUnigramEntry(word, probability, false /* isBeginningOfSentence */, false /* isNotAWord */, - false /* isBlacklisted */, BinaryDictionary.NOT_A_VALID_TIMESTAMP /* timestamp */); + false /* isPossiblyOffensive */, + BinaryDictionary.NOT_A_VALID_TIMESTAMP /* timestamp */); } private static void addBigramWords(final BinaryDictionary binaryDictionary, final String word0, final String word1, final int probability) { - binaryDictionary.addNgramEntry(new PrevWordsInfo(new WordInfo(word0)), word1, probability, + binaryDictionary.addNgramEntry(new NgramContext(new WordInfo(word0)), word1, probability, BinaryDictionary.NOT_A_VALID_TIMESTAMP /* timestamp */); } - private static boolean isValidBigram(final BinaryDictionary binaryDictionary, - final String word0, final String word1) { - return binaryDictionary.isValidNgram(new PrevWordsInfo(new WordInfo(word0)), word1); + private static void addTrigramEntry(final BinaryDictionary binaryDictionary, final String word0, + final String word1, final String word2, final int probability) { + binaryDictionary.addNgramEntry( + new NgramContext(new WordInfo(word1), new WordInfo(word0)), word2, + probability, BinaryDictionary.NOT_A_VALID_TIMESTAMP /* timestamp */); } - private static void removeBigramEntry(final BinaryDictionary binaryDictionary, + private static boolean isValidBigram(final BinaryDictionary binaryDictionary, final String word0, final String word1) { - binaryDictionary.removeNgramEntry(new PrevWordsInfo(new WordInfo(word0)), word1); + return binaryDictionary.isValidNgram(new NgramContext(new WordInfo(word0)), word1); } private static int getBigramProbability(final BinaryDictionary binaryDictionary, final String word0, final String word1) { - return binaryDictionary.getNgramProbability(new PrevWordsInfo(new WordInfo(word0)), word1); + return binaryDictionary.getNgramProbability(new NgramContext(new WordInfo(word0)), word1); } - public void testAddUnigramWord() { - for (final int formatVersion : DICT_FORMAT_VERSIONS) { - testAddUnigramWord(formatVersion); - } + private static int getTrigramProbability(final BinaryDictionary binaryDictionary, + final String word0, final String word1, final String word2) { + return binaryDictionary.getNgramProbability( + new NgramContext(new WordInfo(word1), new WordInfo(word0)), word2); } - private void testAddUnigramWord(final int formatVersion) { - File dictFile = null; - try { - dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary", formatVersion); - } 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 */); - + public void testAddUnigramWord() { + final BinaryDictionary binaryDictionary = getEmptyBinaryDictionary(FormatSpec.VERSION403); final int probability = 100; addUnigramWord(binaryDictionary, "aaa", probability); // Reallocate and create. @@ -263,30 +234,13 @@ public class BinaryDictionaryTests extends AndroidTestCase { assertEquals(probability, binaryDictionary.getFrequency("aaaa")); assertEquals(probability, binaryDictionary.getFrequency("a")); assertEquals(updatedProbability, binaryDictionary.getFrequency("aaa")); - - dictFile.delete(); } public void testRandomlyAddUnigramWord() { - for (final int formatVersion : DICT_FORMAT_VERSIONS) { - testRandomlyAddUnigramWord(formatVersion); - } - } - - private void testRandomlyAddUnigramWord(final int formatVersion) { final int wordCount = 1000; final int codePointSetSize = 50; final long seed = System.currentTimeMillis(); - - File dictFile = null; - try { - dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary", formatVersion); - } 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 BinaryDictionary binaryDictionary = getEmptyBinaryDictionary(FormatSpec.VERSION403); final HashMap<String, Integer> probabilityMap = new HashMap<>(); // Test a word that isn't contained within the dictionary. @@ -302,25 +256,10 @@ public class BinaryDictionaryTests extends AndroidTestCase { for (String word : probabilityMap.keySet()) { assertEquals(word, (int)probabilityMap.get(word), binaryDictionary.getFrequency(word)); } - dictFile.delete(); } public void testAddBigramWords() { - for (final int formatVersion : DICT_FORMAT_VERSIONS) { - testAddBigramWords(formatVersion); - } - } - - private void testAddBigramWords(final int formatVersion) { - File dictFile = null; - try { - dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary", formatVersion); - } 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 BinaryDictionary binaryDictionary = getEmptyBinaryDictionary(FormatSpec.VERSION403); final int unigramProbability = 100; final int bigramProbability = 150; @@ -337,18 +276,14 @@ public class BinaryDictionaryTests extends AndroidTestCase { assertTrue(isValidBigram(binaryDictionary, "aaa", "bcc")); assertTrue(isValidBigram(binaryDictionary, "abb", "aaa")); assertTrue(isValidBigram(binaryDictionary, "abb", "bcc")); - if (canCheckBigramProbability(formatVersion)) { - assertEquals(bigramProbability, getBigramProbability(binaryDictionary, "aaa", "abb")); - assertEquals(bigramProbability, getBigramProbability(binaryDictionary, "aaa", "bcc")); - assertEquals(bigramProbability, getBigramProbability(binaryDictionary, "abb", "aaa")); - assertEquals(bigramProbability, getBigramProbability(binaryDictionary, "abb", "bcc")); - } + assertEquals(bigramProbability, getBigramProbability(binaryDictionary, "aaa", "abb")); + assertEquals(bigramProbability, getBigramProbability(binaryDictionary, "aaa", "bcc")); + assertEquals(bigramProbability, getBigramProbability(binaryDictionary, "abb", "aaa")); + assertEquals(bigramProbability, getBigramProbability(binaryDictionary, "abb", "bcc")); addBigramWords(binaryDictionary, "aaa", "abb", updatedBigramProbability); - if (canCheckBigramProbability(formatVersion)) { - assertEquals(updatedBigramProbability, - getBigramProbability(binaryDictionary, "aaa", "abb")); - } + assertEquals(updatedBigramProbability, + getBigramProbability(binaryDictionary, "aaa", "abb")); assertFalse(isValidBigram(binaryDictionary, "bcc", "aaa")); assertFalse(isValidBigram(binaryDictionary, "bcc", "bbc")); @@ -368,43 +303,21 @@ public class BinaryDictionaryTests extends AndroidTestCase { addUnigramWord(binaryDictionary, "abc", unigramProbability); addUnigramWord(binaryDictionary, "f", unigramProbability); - if (canCheckBigramProbability(formatVersion)) { - assertEquals(bigramProbability, - getBigramProbability(binaryDictionary, "abcde", "fghij")); - } + assertEquals(bigramProbability, getBigramProbability(binaryDictionary, "abcde", "fghij")); assertEquals(Dictionary.NOT_A_PROBABILITY, getBigramProbability(binaryDictionary, "abcde", "fgh")); addBigramWords(binaryDictionary, "abcde", "fghij", updatedBigramProbability); - if (canCheckBigramProbability(formatVersion)) { - assertEquals(updatedBigramProbability, - getBigramProbability(binaryDictionary, "abcde", "fghij")); - } - - dictFile.delete(); + assertEquals(updatedBigramProbability, + getBigramProbability(binaryDictionary, "abcde", "fghij")); } public void testRandomlyAddBigramWords() { - for (final int formatVersion : DICT_FORMAT_VERSIONS) { - testRandomlyAddBigramWords(formatVersion); - } - } - - private void testRandomlyAddBigramWords(final int formatVersion) { final int wordCount = 100; final int bigramCount = 1000; final int codePointSetSize = 50; final long seed = System.currentTimeMillis(); final Random random = new Random(seed); - - File dictFile = null; - try { - dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary", formatVersion); - } 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 BinaryDictionary binaryDictionary = getEmptyBinaryDictionary(FormatSpec.VERSION403); final ArrayList<String> words = new ArrayList<>(); final ArrayList<Pair<String, String>> bigramWords = new ArrayList<>(); @@ -439,83 +352,40 @@ public class BinaryDictionaryTests extends AndroidTestCase { final int bigramProbability = bigramProbabilities.get(bigram); assertEquals(bigramProbability != Dictionary.NOT_A_PROBABILITY, isValidBigram(binaryDictionary, bigram.first, bigram.second)); - if (canCheckBigramProbability(formatVersion)) { - assertEquals(bigramProbability, - getBigramProbability(binaryDictionary, bigram.first, bigram.second)); - } - } - - dictFile.delete(); - } - - public void testRemoveBigramWords() { - for (final int formatVersion : DICT_FORMAT_VERSIONS) { - testRemoveBigramWords(formatVersion); + assertEquals(bigramProbability, + getBigramProbability(binaryDictionary, bigram.first, bigram.second)); } } - private void testRemoveBigramWords(final int formatVersion) { - File dictFile = null; - try { - dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary", formatVersion); - } 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 */); + public void testAddTrigramWords() { + final BinaryDictionary binaryDictionary = getEmptyBinaryDictionary(FormatSpec.VERSION403); final int unigramProbability = 100; - final int bigramProbability = 150; + final int trigramProbability = 150; + final int updatedTrigramProbability = 200; addUnigramWord(binaryDictionary, "aaa", unigramProbability); addUnigramWord(binaryDictionary, "abb", unigramProbability); addUnigramWord(binaryDictionary, "bcc", unigramProbability); - addBigramWords(binaryDictionary, "aaa", "abb", bigramProbability); - addBigramWords(binaryDictionary, "aaa", "bcc", bigramProbability); - addBigramWords(binaryDictionary, "abb", "aaa", bigramProbability); - addBigramWords(binaryDictionary, "abb", "bcc", bigramProbability); - - assertTrue(isValidBigram(binaryDictionary, "aaa", "abb")); - assertTrue(isValidBigram(binaryDictionary, "aaa", "bcc")); - assertTrue(isValidBigram(binaryDictionary, "abb", "aaa")); - assertTrue(isValidBigram(binaryDictionary, "abb", "bcc")); - - removeBigramEntry(binaryDictionary, "aaa", "abb"); - assertFalse(isValidBigram(binaryDictionary, "aaa", "abb")); - addBigramWords(binaryDictionary, "aaa", "abb", bigramProbability); - assertTrue(isValidBigram(binaryDictionary, "aaa", "abb")); + addBigramWords(binaryDictionary, "abb", "bcc", 10); + addBigramWords(binaryDictionary, "abb", "aaa", 10); - removeBigramEntry(binaryDictionary, "aaa", "bcc"); - assertFalse(isValidBigram(binaryDictionary, "aaa", "bcc")); - removeBigramEntry(binaryDictionary, "abb", "aaa"); - assertFalse(isValidBigram(binaryDictionary, "abb", "aaa")); - removeBigramEntry(binaryDictionary, "abb", "bcc"); - assertFalse(isValidBigram(binaryDictionary, "abb", "bcc")); + addTrigramEntry(binaryDictionary, "aaa", "abb", "bcc", trigramProbability); + addTrigramEntry(binaryDictionary, "bcc", "abb", "aaa", trigramProbability); - removeBigramEntry(binaryDictionary, "aaa", "abb"); - // Test remove non-existing bigram operation. - removeBigramEntry(binaryDictionary, "aaa", "abb"); - removeBigramEntry(binaryDictionary, "bcc", "aaa"); + assertEquals(trigramProbability, + getTrigramProbability(binaryDictionary, "aaa", "abb", "bcc")); + assertEquals(trigramProbability, + getTrigramProbability(binaryDictionary, "bcc", "abb", "aaa")); + assertFalse(isValidBigram(binaryDictionary, "aaa", "abb")); - dictFile.delete(); + addTrigramEntry(binaryDictionary, "bcc", "abb", "aaa", updatedTrigramProbability); + assertEquals(updatedTrigramProbability, + getTrigramProbability(binaryDictionary, "bcc", "abb", "aaa")); } public void testFlushDictionary() { - for (final int formatVersion : DICT_FORMAT_VERSIONS) { - testFlushDictionary(formatVersion); - } - } - - private void testFlushDictionary(final int formatVersion) { - File dictFile = null; - try { - dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary", formatVersion); - } 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 File dictFile = createEmptyDictionaryAndGetFile(FormatSpec.VERSION403); + BinaryDictionary binaryDictionary = getBinaryDictionary(dictFile); final int probability = 100; addUnigramWord(binaryDictionary, "aaa", probability); @@ -535,42 +405,21 @@ public class BinaryDictionaryTests extends AndroidTestCase { binaryDictionary.flush(); binaryDictionary.close(); - binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(), - 0 /* offset */, dictFile.length(), true /* useFullEditDistance */, - Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */); - + binaryDictionary = getBinaryDictionary(dictFile); assertEquals(probability, binaryDictionary.getFrequency("aaa")); assertEquals(probability, binaryDictionary.getFrequency("abcd")); addUnigramWord(binaryDictionary, "bcde", probability); binaryDictionary.flush(); binaryDictionary.close(); - binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(), - 0 /* offset */, dictFile.length(), true /* useFullEditDistance */, - Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */); + binaryDictionary = getBinaryDictionary(dictFile); assertEquals(probability, binaryDictionary.getFrequency("bcde")); binaryDictionary.close(); - - dictFile.delete(); } public void testFlushWithGCDictionary() { - for (final int formatVersion : DICT_FORMAT_VERSIONS) { - testFlushWithGCDictionary(formatVersion); - } - } - - private void testFlushWithGCDictionary(final int formatVersion) { - File dictFile = null; - try { - dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary", formatVersion); - } 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 File dictFile = createEmptyDictionaryAndGetFile(FormatSpec.VERSION403); + BinaryDictionary binaryDictionary = getBinaryDictionary(dictFile); final int unigramProbability = 100; final int bigramProbability = 150; addUnigramWord(binaryDictionary, "aaa", unigramProbability); @@ -583,51 +432,30 @@ public class BinaryDictionaryTests extends AndroidTestCase { binaryDictionary.flushWithGC(); binaryDictionary.close(); - binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(), - 0 /* offset */, dictFile.length(), true /* useFullEditDistance */, - Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */); + binaryDictionary = getBinaryDictionary(dictFile); assertEquals(unigramProbability, binaryDictionary.getFrequency("aaa")); assertEquals(unigramProbability, binaryDictionary.getFrequency("abb")); assertEquals(unigramProbability, binaryDictionary.getFrequency("bcc")); - if (canCheckBigramProbability(formatVersion)) { - assertEquals(bigramProbability, getBigramProbability(binaryDictionary, "aaa", "abb")); - assertEquals(bigramProbability, getBigramProbability(binaryDictionary, "aaa", "bcc")); - assertEquals(bigramProbability, getBigramProbability(binaryDictionary, "abb", "aaa")); - assertEquals(bigramProbability, getBigramProbability(binaryDictionary, "abb", "bcc")); - } + assertEquals(bigramProbability, getBigramProbability(binaryDictionary, "aaa", "abb")); + assertEquals(bigramProbability, getBigramProbability(binaryDictionary, "aaa", "bcc")); + assertEquals(bigramProbability, getBigramProbability(binaryDictionary, "abb", "aaa")); + assertEquals(bigramProbability, getBigramProbability(binaryDictionary, "abb", "bcc")); assertFalse(isValidBigram(binaryDictionary, "bcc", "aaa")); assertFalse(isValidBigram(binaryDictionary, "bcc", "bbc")); assertFalse(isValidBigram(binaryDictionary, "aaa", "aaa")); binaryDictionary.flushWithGC(); binaryDictionary.close(); - - dictFile.delete(); } public void testAddBigramWordsAndFlashWithGC() { - for (final int formatVersion : DICT_FORMAT_VERSIONS) { - testAddBigramWordsAndFlashWithGC(formatVersion); - } - } - - // TODO: Evaluate performance of GC - private void testAddBigramWordsAndFlashWithGC(final int formatVersion) { final int wordCount = 100; final int bigramCount = 1000; final int codePointSetSize = 30; final long seed = System.currentTimeMillis(); final Random random = new Random(seed); - File dictFile = null; - try { - dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary", formatVersion); - } 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 File dictFile = createEmptyDictionaryAndGetFile(FormatSpec.VERSION403); + BinaryDictionary binaryDictionary = getBinaryDictionary(dictFile); final ArrayList<String> words = new ArrayList<>(); final ArrayList<Pair<String, String>> bigramWords = new ArrayList<>(); @@ -660,52 +488,37 @@ public class BinaryDictionaryTests extends AndroidTestCase { binaryDictionary.flushWithGC(); binaryDictionary.close(); - binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(), - 0 /* offset */, dictFile.length(), true /* useFullEditDistance */, - Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */); - + binaryDictionary = getBinaryDictionary(dictFile); for (final Pair<String, String> bigram : bigramWords) { final int bigramProbability = bigramProbabilities.get(bigram); assertEquals(bigramProbability != Dictionary.NOT_A_PROBABILITY, isValidBigram(binaryDictionary, bigram.first, bigram.second)); - if (canCheckBigramProbability(formatVersion)) { - assertEquals(bigramProbability, - getBigramProbability(binaryDictionary, bigram.first, bigram.second)); - } + assertEquals(bigramProbability, + getBigramProbability(binaryDictionary, bigram.first, bigram.second)); } - - dictFile.delete(); } public void testRandomOperationsAndFlashWithGC() { - for (final int formatVersion : DICT_FORMAT_VERSIONS) { - testRandomOperationsAndFlashWithGC(formatVersion); - } - } + final int maxUnigramCount = 5000; + final int maxBigramCount = 10000; + final HashMap<String, String> attributeMap = new HashMap<>(); + attributeMap.put(DictionaryHeader.MAX_UNIGRAM_COUNT_KEY, String.valueOf(maxUnigramCount)); + attributeMap.put(DictionaryHeader.MAX_BIGRAM_COUNT_KEY, String.valueOf(maxBigramCount)); - private void testRandomOperationsAndFlashWithGC(final int formatVersion) { 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 long seed = System.currentTimeMillis(); final Random random = new Random(seed); + final File dictFile = createEmptyDictionaryWithAttributesAndGetFile(FormatSpec.VERSION403, + attributeMap); + BinaryDictionary binaryDictionary = getBinaryDictionary(dictFile); - File dictFile = null; - try { - dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary", formatVersion); - } 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<>(); final ArrayList<Pair<String, String>> bigramWords = new ArrayList<>(); final int[] codePointSet = CodePointUtils.generateCodePointSet(codePointSetSize, random); @@ -722,9 +535,7 @@ public class BinaryDictionaryTests extends AndroidTestCase { 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 */); + binaryDictionary = getBinaryDictionary(dictFile); for (int opCount = 0; opCount < operationCountInEachIteration; opCount++) { // Add unigram. if (random.nextFloat() < addUnigramProb) { @@ -754,14 +565,6 @@ public class BinaryDictionaryTests extends AndroidTestCase { bigramProbabilities.put(bigram, bigramProbability); addBigramWords(binaryDictionary, 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); - removeBigramEntry(binaryDictionary, bigram.first, bigram.second); - } } // Test whether the all unigram operations are collectlly handled. @@ -775,45 +578,29 @@ public class BinaryDictionaryTests extends AndroidTestCase { final Pair<String, String> bigram = bigramWords.get(i); final int probability; if (bigramProbabilities.containsKey(bigram)) { - final int bigramProbability = bigramProbabilities.get(bigram); - probability = bigramProbability; + probability = bigramProbabilities.get(bigram); } else { probability = Dictionary.NOT_A_PROBABILITY; } - if (canCheckBigramProbability(formatVersion)) { - assertEquals(probability, - getBigramProbability(binaryDictionary, bigram.first, bigram.second)); - } + assertEquals(probability, + getBigramProbability(binaryDictionary, bigram.first, bigram.second)); assertEquals(probability != Dictionary.NOT_A_PROBABILITY, isValidBigram(binaryDictionary, bigram.first, bigram.second)); } binaryDictionary.flushWithGC(); binaryDictionary.close(); } - - dictFile.delete(); } public void testAddManyUnigramsAndFlushWithGC() { - for (final int formatVersion : DICT_FORMAT_VERSIONS) { - testAddManyUnigramsAndFlushWithGC(formatVersion); - } - } - - private void testAddManyUnigramsAndFlushWithGC(final int formatVersion) { final int flashWithGCIterationCount = 3; final int codePointSetSize = 50; final long seed = System.currentTimeMillis(); final Random random = new Random(seed); - File dictFile = null; - try { - dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary", formatVersion); - } catch (IOException e) { - fail("IOException while writing an initial dictionary : " + e); - } + final File dictFile = createEmptyDictionaryAndGetFile(FormatSpec.VERSION403); final ArrayList<String> words = new ArrayList<>(); final HashMap<String, Integer> unigramProbabilities = new HashMap<>(); @@ -821,9 +608,7 @@ public class BinaryDictionaryTests extends AndroidTestCase { 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 */); + binaryDictionary = getBinaryDictionary(dictFile); while(!binaryDictionary.needsToRunGC(true /* mindsBlockByGC */)) { final String word = CodePointUtils.generateWord(random, codePointSet); words.add(word); @@ -841,30 +626,23 @@ public class BinaryDictionaryTests extends AndroidTestCase { binaryDictionary.flushWithGC(); binaryDictionary.close(); } - - dictFile.delete(); } public void testUnigramAndBigramCount() { - for (final int formatVersion : DICT_FORMAT_VERSIONS) { - testUnigramAndBigramCount(formatVersion); - } - } + final int maxUnigramCount = 5000; + final int maxBigramCount = 10000; + final HashMap<String, String> attributeMap = new HashMap<>(); + attributeMap.put(DictionaryHeader.MAX_UNIGRAM_COUNT_KEY, String.valueOf(maxUnigramCount)); + attributeMap.put(DictionaryHeader.MAX_BIGRAM_COUNT_KEY, String.valueOf(maxBigramCount)); - private void testUnigramAndBigramCount(final int formatVersion) { final int flashWithGCIterationCount = 10; final int codePointSetSize = 50; final int unigramCountPerIteration = 1000; final int bigramCountPerIteration = 2000; final long seed = System.currentTimeMillis(); final Random random = new Random(seed); - - File dictFile = null; - try { - dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary", formatVersion); - } catch (IOException e) { - fail("IOException while writing an initial dictionary : " + e); - } + final File dictFile = createEmptyDictionaryWithAttributesAndGetFile(FormatSpec.VERSION403, + attributeMap); final ArrayList<String> words = new ArrayList<>(); final HashSet<Pair<String, String>> bigrams = new HashSet<>(); @@ -872,9 +650,7 @@ public class BinaryDictionaryTests extends AndroidTestCase { 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 */); + binaryDictionary = getBinaryDictionary(dictFile); for (int j = 0; j < unigramCountPerIteration; j++) { final String word = CodePointUtils.generateWord(random, codePointSet); words.add(word); @@ -892,108 +668,31 @@ public class BinaryDictionaryTests extends AndroidTestCase { addBigramWords(binaryDictionary, word0, word1, bigramProbability); } assertEquals(new HashSet<>(words).size(), Integer.parseInt( - binaryDictionary.getPropertyForTest(BinaryDictionary.UNIGRAM_COUNT_QUERY))); + binaryDictionary.getPropertyForGettingStats( + BinaryDictionary.UNIGRAM_COUNT_QUERY))); assertEquals(new HashSet<>(bigrams).size(), Integer.parseInt( - binaryDictionary.getPropertyForTest(BinaryDictionary.BIGRAM_COUNT_QUERY))); + binaryDictionary.getPropertyForGettingStats( + BinaryDictionary.BIGRAM_COUNT_QUERY))); binaryDictionary.flushWithGC(); assertEquals(new HashSet<>(words).size(), Integer.parseInt( - binaryDictionary.getPropertyForTest(BinaryDictionary.UNIGRAM_COUNT_QUERY))); + binaryDictionary.getPropertyForGettingStats( + BinaryDictionary.UNIGRAM_COUNT_QUERY))); assertEquals(new HashSet<>(bigrams).size(), Integer.parseInt( - binaryDictionary.getPropertyForTest(BinaryDictionary.BIGRAM_COUNT_QUERY))); + binaryDictionary.getPropertyForGettingStats( + BinaryDictionary.BIGRAM_COUNT_QUERY))); binaryDictionary.close(); } - - dictFile.delete(); - } - - public void testAddMultipleDictionaryEntries() { - for (final int formatVersion : DICT_FORMAT_VERSIONS) { - testAddMultipleDictionaryEntries(formatVersion); - } - } - - private void testAddMultipleDictionaryEntries(final int formatVersion) { - final int codePointSetSize = 20; - final int lmParamCount = 1000; - final double bigramContinueRate = 0.9; - final long seed = System.currentTimeMillis(); - final Random random = new Random(seed); - - File dictFile = null; - try { - dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary", formatVersion); - } catch (IOException e) { - fail("IOException while writing an initial dictionary : " + e); - } - - final int[] codePointSet = CodePointUtils.generateCodePointSet(codePointSetSize, random); - final HashMap<String, Integer> unigramProbabilities = new HashMap<>(); - final HashMap<Pair<String, String>, Integer> bigramProbabilities = new HashMap<>(); - - final LanguageModelParam[] languageModelParams = new LanguageModelParam[lmParamCount]; - String prevWord = null; - for (int i = 0; i < languageModelParams.length; i++) { - final String word = CodePointUtils.generateWord(random, codePointSet); - final int probability = random.nextInt(0xFF); - final int bigramProbability = probability + random.nextInt(0xFF - probability); - unigramProbabilities.put(word, probability); - if (prevWord == null) { - languageModelParams[i] = new LanguageModelParam(word, probability, - BinaryDictionary.NOT_A_VALID_TIMESTAMP); - } else { - languageModelParams[i] = new LanguageModelParam(prevWord, word, probability, - bigramProbability, BinaryDictionary.NOT_A_VALID_TIMESTAMP); - bigramProbabilities.put(new Pair<>(prevWord, word), - bigramProbability); - } - prevWord = (random.nextDouble() < bigramContinueRate) ? word : null; - } - - final BinaryDictionary binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(), - 0 /* offset */, dictFile.length(), true /* useFullEditDistance */, - Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */); - binaryDictionary.addMultipleDictionaryEntries(languageModelParams); - - for (Map.Entry<String, Integer> entry : unigramProbabilities.entrySet()) { - assertEquals((int)entry.getValue(), binaryDictionary.getFrequency(entry.getKey())); - } - - for (Map.Entry<Pair<String, String>, Integer> entry : bigramProbabilities.entrySet()) { - final String word0 = entry.getKey().first; - final String word1 = entry.getKey().second; - final int bigramProbability = entry.getValue(); - assertEquals(bigramProbability != Dictionary.NOT_A_PROBABILITY, - isValidBigram(binaryDictionary, word0, word1)); - if (canCheckBigramProbability(formatVersion)) { - assertEquals(bigramProbability, - getBigramProbability(binaryDictionary, word0, word1)); - } - } } public void testGetWordProperties() { - for (final int formatVersion : DICT_FORMAT_VERSIONS) { - testGetWordProperties(formatVersion); - } - } - - private void testGetWordProperties(final int formatVersion) { final long seed = System.currentTimeMillis(); final Random random = new Random(seed); final int UNIGRAM_COUNT = 1000; final int BIGRAM_COUNT = 1000; final int codePointSetSize = 20; final int[] codePointSet = CodePointUtils.generateCodePointSet(codePointSetSize, random); - - File dictFile = null; - try { - dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary", formatVersion); - } catch (IOException e) { - fail("IOException while writing an initial dictionary : " + e); - } - final BinaryDictionary binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(), - 0 /* offset */, dictFile.length(), true /* useFullEditDistance */, - Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */); + final File dictFile = createEmptyDictionaryAndGetFile(FormatSpec.VERSION403); + final BinaryDictionary binaryDictionary = getBinaryDictionary(dictFile); final WordProperty invalidWordProperty = binaryDictionary.getWordProperty("dummyWord", false /* isBeginningOfSentence */); @@ -1008,11 +707,10 @@ public class BinaryDictionaryTests extends AndroidTestCase { final String word = CodePointUtils.generateWord(random, codePointSet); final int unigramProbability = random.nextInt(0xFF); final boolean isNotAWord = random.nextBoolean(); - final boolean isBlacklisted = random.nextBoolean(); + final boolean isPossiblyOffensive = random.nextBoolean(); // TODO: Add tests for historical info. binaryDictionary.addUnigramEntry(word, unigramProbability, - null /* shortcutTarget */, BinaryDictionary.NOT_A_PROBABILITY, - false /* isBeginningOfSentence */, isNotAWord, isBlacklisted, + false /* isBeginningOfSentence */, isNotAWord, isPossiblyOffensive, BinaryDictionary.NOT_A_VALID_TIMESTAMP); if (binaryDictionary.needsToRunGC(false /* mindsBlockByGC */)) { binaryDictionary.flushWithGC(); @@ -1024,11 +722,9 @@ public class BinaryDictionaryTests extends AndroidTestCase { assertEquals(word, wordProperty.mWord); assertTrue(wordProperty.isValid()); assertEquals(isNotAWord, wordProperty.mIsNotAWord); - assertEquals(isBlacklisted, wordProperty.mIsBlacklistEntry); - assertEquals(false, wordProperty.mHasBigrams); - assertEquals(false, wordProperty.mHasShortcuts); + assertEquals(isPossiblyOffensive, wordProperty.mIsPossiblyOffensive); + assertEquals(false, wordProperty.mHasNgrams); assertEquals(unigramProbability, wordProperty.mProbabilityInfo.mProbability); - assertTrue(wordProperty.mShortcutTargets.isEmpty()); } for (int i = 0; i < BIGRAM_COUNT; i++) { @@ -1062,41 +758,25 @@ public class BinaryDictionaryTests extends AndroidTestCase { final HashSet<String> bigramWord1s = bigrams.get(word0); final WordProperty wordProperty = binaryDictionary.getWordProperty(word0, false /* isBeginningOfSentence */); - assertEquals(bigramWord1s.size(), wordProperty.mBigrams.size()); - for (int j = 0; j < wordProperty.mBigrams.size(); j++) { - final String word1 = wordProperty.mBigrams.get(j).mWord; + assertEquals(bigramWord1s.size(), wordProperty.mNgrams.size()); + // TODO: Support ngram. + for (final WeightedString bigramTarget : wordProperty.getBigrams()) { + final String word1 = bigramTarget.mWord; assertTrue(bigramWord1s.contains(word1)); - if (canCheckBigramProbability(formatVersion)) { - final int bigramProbability = bigramProbabilities.get(new Pair<>(word0, word1)); - assertEquals(bigramProbability, wordProperty.mBigrams.get(j).getProbability()); - } + final int bigramProbability = bigramProbabilities.get(new Pair<>(word0, word1)); + assertEquals(bigramProbability, bigramTarget.getProbability()); } } } public void testIterateAllWords() { - for (final int formatVersion : DICT_FORMAT_VERSIONS) { - testIterateAllWords(formatVersion); - } - } - - private void testIterateAllWords(final int formatVersion) { final long seed = System.currentTimeMillis(); final Random random = new Random(seed); final int UNIGRAM_COUNT = 1000; final int BIGRAM_COUNT = 1000; final int codePointSetSize = 20; final int[] codePointSet = CodePointUtils.generateCodePointSet(codePointSetSize, random); - - File dictFile = null; - try { - dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary", formatVersion); - } catch (IOException e) { - fail("IOException while writing an initial dictionary : " + e); - } - final BinaryDictionary binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(), - 0 /* offset */, dictFile.length(), true /* useFullEditDistance */, - Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */); + final BinaryDictionary binaryDictionary = getEmptyBinaryDictionary(FormatSpec.VERSION403); final WordProperty invalidWordProperty = binaryDictionary.getWordProperty("dummyWord", false /* isBeginningOfSentence */); @@ -1155,15 +835,16 @@ public class BinaryDictionaryTests extends AndroidTestCase { wordProperty.mProbabilityInfo.mProbability); wordSet.remove(word0); final HashSet<String> bigramWord1s = bigrams.get(word0); - for (int j = 0; j < wordProperty.mBigrams.size(); j++) { - final String word1 = wordProperty.mBigrams.get(j).mWord; - assertTrue(bigramWord1s.contains(word1)); - final Pair<String, String> bigram = new Pair<>(word0, word1); - if (canCheckBigramProbability(formatVersion)) { + // TODO: Support ngram. + if (wordProperty.mHasNgrams) { + for (final WeightedString bigramTarget : wordProperty.getBigrams()) { + final String word1 = bigramTarget.mWord; + assertTrue(bigramWord1s.contains(word1)); + final Pair<String, String> bigram = new Pair<>(word0, word1); final int bigramProbability = bigramProbabilitiesToCheckLater.get(bigram); - assertEquals(bigramProbability, wordProperty.mBigrams.get(j).getProbability()); + assertEquals(bigramProbability, bigramTarget.getProbability()); + bigramSet.remove(bigram); } - bigramSet.remove(bigram); } token = result.mNextToken; } while (token != 0); @@ -1171,346 +852,33 @@ public class BinaryDictionaryTests extends AndroidTestCase { assertTrue(bigramSet.isEmpty()); } - public void testAddShortcuts() { - for (final int formatVersion : DICT_FORMAT_VERSIONS) { - testAddShortcuts(formatVersion); - } - } - - private void testAddShortcuts(final int formatVersion) { - File dictFile = null; - try { - dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary", formatVersion); - } catch (IOException e) { - fail("IOException while writing an initial dictionary : " + e); - } - final BinaryDictionary binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(), - 0 /* offset */, dictFile.length(), true /* useFullEditDistance */, - Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */); - - final int unigramProbability = 100; - final int shortcutProbability = 10; - binaryDictionary.addUnigramEntry("aaa", unigramProbability, "zzz", - shortcutProbability, false /* isBeginningOfSentence */, - false /* isNotAWord */, false /* isBlacklisted */, 0 /* timestamp */); - WordProperty wordProperty = binaryDictionary.getWordProperty("aaa", - false /* isBeginningOfSentence */); - assertEquals(1, wordProperty.mShortcutTargets.size()); - assertEquals("zzz", wordProperty.mShortcutTargets.get(0).mWord); - assertEquals(shortcutProbability, wordProperty.mShortcutTargets.get(0).getProbability()); - final int updatedShortcutProbability = 2; - binaryDictionary.addUnigramEntry("aaa", unigramProbability, "zzz", - updatedShortcutProbability, false /* isBeginningOfSentence */, - false /* isNotAWord */, false /* isBlacklisted */, 0 /* timestamp */); - wordProperty = binaryDictionary.getWordProperty("aaa", - false /* isBeginningOfSentence */); - assertEquals(1, wordProperty.mShortcutTargets.size()); - assertEquals("zzz", wordProperty.mShortcutTargets.get(0).mWord); - assertEquals(updatedShortcutProbability, - wordProperty.mShortcutTargets.get(0).getProbability()); - binaryDictionary.addUnigramEntry("aaa", unigramProbability, "yyy", - shortcutProbability, false /* isBeginningOfSentence */, false /* isNotAWord */, - false /* isBlacklisted */, 0 /* timestamp */); - final HashMap<String, Integer> shortcutTargets = new HashMap<>(); - shortcutTargets.put("zzz", updatedShortcutProbability); - shortcutTargets.put("yyy", shortcutProbability); - wordProperty = binaryDictionary.getWordProperty("aaa", - false /* isBeginningOfSentence */); - assertEquals(2, wordProperty.mShortcutTargets.size()); - for (WeightedString shortcutTarget : wordProperty.mShortcutTargets) { - assertTrue(shortcutTargets.containsKey(shortcutTarget.mWord)); - assertEquals((int)shortcutTargets.get(shortcutTarget.mWord), - shortcutTarget.getProbability()); - shortcutTargets.remove(shortcutTarget.mWord); - } - shortcutTargets.put("zzz", updatedShortcutProbability); - shortcutTargets.put("yyy", shortcutProbability); - binaryDictionary.flushWithGC(); - wordProperty = binaryDictionary.getWordProperty("aaa", - false /* isBeginningOfSentence */); - assertEquals(2, wordProperty.mShortcutTargets.size()); - for (WeightedString shortcutTarget : wordProperty.mShortcutTargets) { - assertTrue(shortcutTargets.containsKey(shortcutTarget.mWord)); - assertEquals((int)shortcutTargets.get(shortcutTarget.mWord), - shortcutTarget.getProbability()); - shortcutTargets.remove(shortcutTarget.mWord); - } - } - - public void testAddManyShortcuts() { - for (final int formatVersion : DICT_FORMAT_VERSIONS) { - testAddManyShortcuts(formatVersion); - } - } - - private void testAddManyShortcuts(final int formatVersion) { - final long seed = System.currentTimeMillis(); - final Random random = new Random(seed); - final int UNIGRAM_COUNT = 1000; - final int SHORTCUT_COUNT = 10000; - final int codePointSetSize = 20; - final int[] codePointSet = CodePointUtils.generateCodePointSet(codePointSetSize, random); - - final ArrayList<String> words = new ArrayList<>(); - final HashMap<String, Integer> unigramProbabilities = new HashMap<>(); - final HashMap<String, HashMap<String, Integer>> shortcutTargets = new HashMap<>(); - - File dictFile = null; - try { - dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary", formatVersion); - } catch (IOException e) { - fail("IOException while writing an initial dictionary : " + e); - } - final BinaryDictionary binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(), - 0 /* offset */, dictFile.length(), true /* useFullEditDistance */, - Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */); - - for (int i = 0; i < UNIGRAM_COUNT; i++) { - final String word = CodePointUtils.generateWord(random, codePointSet); - final int unigramProbability = random.nextInt(0xFF); - addUnigramWord(binaryDictionary, word, unigramProbability); - words.add(word); - unigramProbabilities.put(word, unigramProbability); - if (binaryDictionary.needsToRunGC(true /* mindsBlockByGC */)) { - binaryDictionary.flushWithGC(); - } - } - for (int i = 0; i < SHORTCUT_COUNT; i++) { - final String shortcutTarget = CodePointUtils.generateWord(random, codePointSet); - final int shortcutProbability = random.nextInt(0xF); - final String word = words.get(random.nextInt(words.size())); - final int unigramProbability = unigramProbabilities.get(word); - binaryDictionary.addUnigramEntry(word, unigramProbability, shortcutTarget, - shortcutProbability, false /* isBeginningOfSentence */, false /* isNotAWord */, - false /* isBlacklisted */, 0 /* timestamp */); - if (shortcutTargets.containsKey(word)) { - final HashMap<String, Integer> shortcutTargetsOfWord = shortcutTargets.get(word); - shortcutTargetsOfWord.put(shortcutTarget, shortcutProbability); - } else { - final HashMap<String, Integer> shortcutTargetsOfWord = new HashMap<>(); - shortcutTargetsOfWord.put(shortcutTarget, shortcutProbability); - shortcutTargets.put(word, shortcutTargetsOfWord); - } - if (binaryDictionary.needsToRunGC(true /* mindsBlockByGC */)) { - binaryDictionary.flushWithGC(); - } - } - - for (final String word : words) { - final WordProperty wordProperty = binaryDictionary.getWordProperty(word, - false /* isBeginningOfSentence */); - assertEquals((int)unigramProbabilities.get(word), - wordProperty.mProbabilityInfo.mProbability); - if (!shortcutTargets.containsKey(word)) { - // The word does not have shortcut targets. - continue; - } - assertEquals(shortcutTargets.get(word).size(), wordProperty.mShortcutTargets.size()); - for (final WeightedString shortcutTarget : wordProperty.mShortcutTargets) { - final String targetCodePonts = shortcutTarget.mWord; - assertEquals((int)shortcutTargets.get(word).get(targetCodePonts), - shortcutTarget.getProbability()); - } - } - } - - public void testDictMigration() { - for (final int formatVersion : DICT_FORMAT_VERSIONS) { - testDictMigration(FormatSpec.VERSION4_ONLY_FOR_TESTING, formatVersion); - } - } - - private void testDictMigration(final int fromFormatVersion, final int toFormatVersion) { - File dictFile = null; - try { - dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary", fromFormatVersion); - } catch (IOException e) { - fail("IOException while writing an initial dictionary : " + e); - } - final BinaryDictionary binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(), - 0 /* offset */, dictFile.length(), true /* useFullEditDistance */, - Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */); - final int unigramProbability = 100; - addUnigramWord(binaryDictionary, "aaa", unigramProbability); - addUnigramWord(binaryDictionary, "bbb", unigramProbability); - final int bigramProbability = 150; - addBigramWords(binaryDictionary, "aaa", "bbb", bigramProbability); - final int shortcutProbability = 10; - binaryDictionary.addUnigramEntry("ccc", unigramProbability, "xxx", shortcutProbability, - false /* isBeginningOfSentence */, false /* isNotAWord */, - false /* isBlacklisted */, 0 /* timestamp */); - binaryDictionary.addUnigramEntry("ddd", unigramProbability, null /* shortcutTarget */, - Dictionary.NOT_A_PROBABILITY, false /* isBeginningOfSentence */, - true /* isNotAWord */, true /* isBlacklisted */, 0 /* timestamp */); - binaryDictionary.addNgramEntry(PrevWordsInfo.BEGINNING_OF_SENTENCE, - "aaa", bigramProbability, 0 /* timestamp */); - assertEquals(unigramProbability, binaryDictionary.getFrequency("aaa")); - assertEquals(unigramProbability, binaryDictionary.getFrequency("bbb")); - assertTrue(isValidBigram(binaryDictionary, "aaa", "bbb")); - assertEquals(fromFormatVersion, binaryDictionary.getFormatVersion()); - assertTrue(binaryDictionary.migrateTo(toFormatVersion)); - assertTrue(binaryDictionary.isValidDictionary()); - assertEquals(toFormatVersion, binaryDictionary.getFormatVersion()); - assertEquals(unigramProbability, binaryDictionary.getFrequency("aaa")); - assertEquals(unigramProbability, binaryDictionary.getFrequency("bbb")); - if (canCheckBigramProbability(toFormatVersion)) { - assertEquals(bigramProbability, getBigramProbability(binaryDictionary, "aaa", "bbb")); - assertEquals(bigramProbability, binaryDictionary.getNgramProbability( - PrevWordsInfo.BEGINNING_OF_SENTENCE, "aaa")); - } - assertTrue(isValidBigram(binaryDictionary, "aaa", "bbb")); - WordProperty wordProperty = binaryDictionary.getWordProperty("ccc", - false /* isBeginningOfSentence */); - assertEquals(1, wordProperty.mShortcutTargets.size()); - assertEquals("xxx", wordProperty.mShortcutTargets.get(0).mWord); - wordProperty = binaryDictionary.getWordProperty("ddd", - false /* isBeginningOfSentence */); - assertTrue(wordProperty.mIsBlacklistEntry); - assertTrue(wordProperty.mIsNotAWord); - } - - public void testLargeDictMigration() { - for (final int formatVersion : DICT_FORMAT_VERSIONS) { - testLargeDictMigration(FormatSpec.VERSION4_ONLY_FOR_TESTING, formatVersion); - } - } - - private void testLargeDictMigration(final int fromFormatVersion, final int toFormatVersion) { - final int UNIGRAM_COUNT = 3000; - final int BIGRAM_COUNT = 3000; - final int codePointSetSize = 50; - final long seed = System.currentTimeMillis(); - final Random random = new Random(seed); - - File dictFile = null; - try { - dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary", fromFormatVersion); - } catch (IOException e) { - fail("IOException while writing an initial dictionary : " + e); - } - final BinaryDictionary binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(), - 0 /* offset */, dictFile.length(), true /* useFullEditDistance */, - Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */); - - final ArrayList<String> words = new ArrayList<>(); - final ArrayList<Pair<String, String>> bigrams = new ArrayList<>(); - final int[] codePointSet = CodePointUtils.generateCodePointSet(codePointSetSize, random); - final HashMap<String, Integer> unigramProbabilities = new HashMap<>(); - final HashMap<Pair<String, String>, Integer> bigramProbabilities = new HashMap<>(); - - for (int i = 0; i < UNIGRAM_COUNT; i++) { - final String word = CodePointUtils.generateWord(random, codePointSet); - final int unigramProbability = random.nextInt(0xFF); - addUnigramWord(binaryDictionary, word, unigramProbability); - if (binaryDictionary.needsToRunGC(true /* mindsBlockByGC */)) { - binaryDictionary.flushWithGC(); - } - words.add(word); - unigramProbabilities.put(word, unigramProbability); - } - - for (int i = 0; i < BIGRAM_COUNT; i++) { - final int word0Index = random.nextInt(words.size()); - final int word1Index = random.nextInt(words.size()); - if (word0Index == word1Index) { - continue; - } - final String word0 = words.get(word0Index); - final String word1 = words.get(word1Index); - final int unigramProbability = unigramProbabilities.get(word1); - final int bigramProbability = - random.nextInt(0xFF - unigramProbability) + unigramProbability; - addBigramWords(binaryDictionary, word0, word1, bigramProbability); - if (binaryDictionary.needsToRunGC(true /* mindsBlockByGC */)) { - binaryDictionary.flushWithGC(); - } - final Pair<String, String> bigram = new Pair<>(word0, word1); - bigrams.add(bigram); - bigramProbabilities.put(bigram, bigramProbability); - } - assertTrue(binaryDictionary.migrateTo(toFormatVersion)); - - for (final String word : words) { - assertEquals((int)unigramProbabilities.get(word), binaryDictionary.getFrequency(word)); - } - assertEquals(unigramProbabilities.size(), Integer.parseInt( - binaryDictionary.getPropertyForTest(BinaryDictionary.UNIGRAM_COUNT_QUERY))); - - for (final Pair<String, String> bigram : bigrams) { - if (canCheckBigramProbability(toFormatVersion)) { - assertEquals((int)bigramProbabilities.get(bigram), - getBigramProbability(binaryDictionary, bigram.first, bigram.second)); - } - assertTrue(isValidBigram(binaryDictionary, bigram.first, bigram.second)); - } - assertEquals(bigramProbabilities.size(), Integer.parseInt( - binaryDictionary.getPropertyForTest(BinaryDictionary.BIGRAM_COUNT_QUERY))); + public void testPossiblyOffensiveAttributeMaintained() { + final BinaryDictionary binaryDictionary = + getEmptyBinaryDictionary(FormatSpec.VERSION403); + binaryDictionary.addUnigramEntry("ddd", 100, false, true, true, 0); + WordProperty wordProperty = binaryDictionary.getWordProperty("ddd", false); + assertEquals(true, wordProperty.mIsPossiblyOffensive); } public void testBeginningOfSentence() { - for (final int formatVersion : DICT_FORMAT_VERSIONS) { - if (supportsBeginningOfSentence(formatVersion)) { - testBeginningOfSentence(formatVersion); - } - } - } - - private void testBeginningOfSentence(final int formatVersion) { - File dictFile = null; - try { - dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary", formatVersion); - } catch (IOException e) { - fail("IOException while writing an initial dictionary : " + e); - } - final BinaryDictionary binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(), - 0 /* offset */, dictFile.length(), true /* useFullEditDistance */, - Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */); + final BinaryDictionary binaryDictionary = getEmptyBinaryDictionary(FormatSpec.VERSION403); final int dummyProbability = 0; - final PrevWordsInfo prevWordsInfoBeginningOfSentence = PrevWordsInfo.BEGINNING_OF_SENTENCE; + final NgramContext beginningOfSentenceContext = NgramContext.BEGINNING_OF_SENTENCE; final int bigramProbability = 200; addUnigramWord(binaryDictionary, "aaa", dummyProbability); - binaryDictionary.addNgramEntry(prevWordsInfoBeginningOfSentence, "aaa", bigramProbability, + binaryDictionary.addNgramEntry(beginningOfSentenceContext, "aaa", bigramProbability, BinaryDictionary.NOT_A_VALID_TIMESTAMP /* timestamp */); assertEquals(bigramProbability, - binaryDictionary.getNgramProbability(prevWordsInfoBeginningOfSentence, "aaa")); - binaryDictionary.addNgramEntry(prevWordsInfoBeginningOfSentence, "aaa", bigramProbability, + binaryDictionary.getNgramProbability(beginningOfSentenceContext, "aaa")); + binaryDictionary.addNgramEntry(beginningOfSentenceContext, "aaa", bigramProbability, BinaryDictionary.NOT_A_VALID_TIMESTAMP /* timestamp */); addUnigramWord(binaryDictionary, "bbb", dummyProbability); - binaryDictionary.addNgramEntry(prevWordsInfoBeginningOfSentence, "bbb", bigramProbability, + binaryDictionary.addNgramEntry(beginningOfSentenceContext, "bbb", bigramProbability, BinaryDictionary.NOT_A_VALID_TIMESTAMP /* timestamp */); binaryDictionary.flushWithGC(); assertEquals(bigramProbability, - binaryDictionary.getNgramProbability(prevWordsInfoBeginningOfSentence, "aaa")); + binaryDictionary.getNgramProbability(beginningOfSentenceContext, "aaa")); assertEquals(bigramProbability, - binaryDictionary.getNgramProbability(prevWordsInfoBeginningOfSentence, "bbb")); - } - - public void testGetMaxFrequencyOfExactMatches() { - for (final int formatVersion : DICT_FORMAT_VERSIONS) { - testGetMaxFrequencyOfExactMatches(formatVersion); - } - } - - private void testGetMaxFrequencyOfExactMatches(final int formatVersion) { - File dictFile = null; - try { - dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary", formatVersion); - } catch (IOException e) { - fail("IOException while writing an initial dictionary : " + e); - } - final BinaryDictionary binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(), - 0 /* offset */, dictFile.length(), true /* useFullEditDistance */, - Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */); - addUnigramWord(binaryDictionary, "abc", 10); - addUnigramWord(binaryDictionary, "aBc", 15); - assertEquals(15, binaryDictionary.getMaxFrequencyOfExactMatches("abc")); - addUnigramWord(binaryDictionary, "ab'c", 20); - assertEquals(20, binaryDictionary.getMaxFrequencyOfExactMatches("abc")); - addUnigramWord(binaryDictionary, "a-b-c", 25); - assertEquals(25, binaryDictionary.getMaxFrequencyOfExactMatches("abc")); - addUnigramWord(binaryDictionary, "ab-'-'-'-c", 30); - assertEquals(30, binaryDictionary.getMaxFrequencyOfExactMatches("abc")); - addUnigramWord(binaryDictionary, "ab c", 255); - assertEquals(30, binaryDictionary.getMaxFrequencyOfExactMatches("abc")); + binaryDictionary.getNgramProbability(beginningOfSentenceContext, "bbb")); } } diff --git a/tests/src/com/android/inputmethod/latin/BlueUnderlineTests.java b/tests/src/com/android/inputmethod/latin/BlueUnderlineTests.java index 6e894decf..afb94ef05 100644 --- a/tests/src/com/android/inputmethod/latin/BlueUnderlineTests.java +++ b/tests/src/com/android/inputmethod/latin/BlueUnderlineTests.java @@ -20,6 +20,8 @@ import android.test.suitebuilder.annotation.LargeTest; import android.text.style.SuggestionSpan; import android.text.style.UnderlineSpan; +import com.android.inputmethod.latin.common.Constants; + @LargeTest public class BlueUnderlineTests extends InputTestsBase { @@ -28,7 +30,7 @@ public class BlueUnderlineTests extends InputTestsBase { final int EXPECTED_SPAN_START = 0; final int EXPECTED_SPAN_END = 4; type(STRING_TO_TYPE); - sleep(DELAY_TO_WAIT_FOR_UNDERLINE); + sleep(DELAY_TO_WAIT_FOR_UNDERLINE_MILLIS); runMessages(); final SpanGetter span = new SpanGetter(mEditText.getText(), SuggestionSpan.class); assertEquals("show blue underline, span start", EXPECTED_SPAN_START, span.mStart); @@ -37,12 +39,12 @@ public class BlueUnderlineTests extends InputTestsBase { } public void testBlueUnderlineDisappears() { - final String STRING_1_TO_TYPE = "tgis"; - final String STRING_2_TO_TYPE = "q"; + final String STRING_1_TO_TYPE = "tqis"; + final String STRING_2_TO_TYPE = "g"; final int EXPECTED_SPAN_START = 0; final int EXPECTED_SPAN_END = 5; type(STRING_1_TO_TYPE); - sleep(DELAY_TO_WAIT_FOR_UNDERLINE); + sleep(DELAY_TO_WAIT_FOR_UNDERLINE_MILLIS); runMessages(); type(STRING_2_TO_TYPE); // We haven't have time to look into the dictionary yet, so the line should still be @@ -51,7 +53,7 @@ public class BlueUnderlineTests extends InputTestsBase { assertEquals("extend blue underline, span start", EXPECTED_SPAN_START, spanBefore.mStart); assertEquals("extend blue underline, span end", EXPECTED_SPAN_END, spanBefore.mEnd); assertTrue("extend blue underline, span color", spanBefore.isAutoCorrectionIndicator()); - sleep(DELAY_TO_WAIT_FOR_UNDERLINE); + sleep(DELAY_TO_WAIT_FOR_UNDERLINE_MILLIS); runMessages(); // Now we have been able to re-evaluate the word, there shouldn't be an auto-correction span final SpanGetter spanAfter = new SpanGetter(mEditText.getText(), SuggestionSpan.class); @@ -61,22 +63,21 @@ public class BlueUnderlineTests extends InputTestsBase { public void testBlueUnderlineOnBackspace() { final String STRING_TO_TYPE = "tgis"; final int typedLength = STRING_TO_TYPE.length(); - final int EXPECTED_SUGGESTION_SPAN_START = -1; final int EXPECTED_UNDERLINE_SPAN_START = 0; - final int EXPECTED_UNDERLINE_SPAN_END = 4; + final int EXPECTED_UNDERLINE_SPAN_END = 3; type(STRING_TO_TYPE); - sleep(DELAY_TO_WAIT_FOR_UNDERLINE); + sleep(DELAY_TO_WAIT_FOR_UNDERLINE_MILLIS); runMessages(); type(Constants.CODE_SPACE); // typedLength + 1 because we also typed a space mLatinIME.onUpdateSelection(0, 0, typedLength + 1, typedLength + 1, -1, -1); - sleep(DELAY_TO_WAIT_FOR_UNDERLINE); + sleep(DELAY_TO_WAIT_FOR_UNDERLINE_MILLIS); runMessages(); type(Constants.CODE_DELETE); - sleep(DELAY_TO_WAIT_FOR_UNDERLINE); + sleep(DELAY_TO_WAIT_FOR_UNDERLINE_MILLIS); runMessages(); type(Constants.CODE_DELETE); - sleep(DELAY_TO_WAIT_FOR_UNDERLINE); + sleep(DELAY_TO_WAIT_FOR_UNDERLINE_MILLIS); runMessages(); final SpanGetter suggestionSpan = new SpanGetter(mEditText.getText(), SuggestionSpan.class); assertFalse("show no blue underline after backspace, span should not be the auto-" @@ -93,7 +94,7 @@ public class BlueUnderlineTests extends InputTestsBase { final int typedLength = STRING_TO_TYPE.length(); final int NEW_CURSOR_POSITION = 0; type(STRING_TO_TYPE); - sleep(DELAY_TO_WAIT_FOR_UNDERLINE); + sleep(DELAY_TO_WAIT_FOR_UNDERLINE_MILLIS); // Simulate the onUpdateSelection() event mLatinIME.onUpdateSelection(0, 0, typedLength, typedLength, -1, -1); runMessages(); @@ -103,7 +104,7 @@ public class BlueUnderlineTests extends InputTestsBase { mInputConnection.setSelection(NEW_CURSOR_POSITION, NEW_CURSOR_POSITION); mLatinIME.onUpdateSelection(typedLength, typedLength, NEW_CURSOR_POSITION, NEW_CURSOR_POSITION, -1, -1); - sleep(DELAY_TO_WAIT_FOR_UNDERLINE); + sleep(DELAY_TO_WAIT_FOR_UNDERLINE_MILLIS); runMessages(); final SpanGetter span = new SpanGetter(mEditText.getText(), SuggestionSpan.class); assertFalse("blue underline removed when cursor is moved", @@ -113,7 +114,7 @@ public class BlueUnderlineTests extends InputTestsBase { public void testComposingStopsOnSpace() { final String STRING_TO_TYPE = "this "; type(STRING_TO_TYPE); - sleep(DELAY_TO_WAIT_FOR_UNDERLINE); + sleep(DELAY_TO_WAIT_FOR_UNDERLINE_MILLIS); // Simulate the onUpdateSelection() event mLatinIME.onUpdateSelection(0, 0, STRING_TO_TYPE.length(), STRING_TO_TYPE.length(), -1, -1); runMessages(); diff --git a/tests/src/com/android/inputmethod/latin/ContactsContentObserverTest.java b/tests/src/com/android/inputmethod/latin/ContactsContentObserverTest.java new file mode 100644 index 000000000..f90a18bf1 --- /dev/null +++ b/tests/src/com/android/inputmethod/latin/ContactsContentObserverTest.java @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.inputmethod.latin; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.validateMockitoUsage; +import static org.mockito.Mockito.when; + +import android.content.Context; +import android.provider.ContactsContract.Contacts; +import android.test.suitebuilder.annotation.SmallTest; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.util.ArrayList; + +/** + * Tests for {@link ContactsContentObserver}. + */ +@SmallTest +public class ContactsContentObserverTest { + private static final int UPDATED_CONTACT_COUNT = 10; + private static final int STALE_CONTACT_COUNT = 8; + private static final ArrayList<String> STALE_NAMES_LIST = new ArrayList<>(); + private static final ArrayList<String> UPDATED_NAMES_LIST = new ArrayList<>(); + + static { + STALE_NAMES_LIST.add("Larry Page"); + STALE_NAMES_LIST.add("Roger Federer"); + UPDATED_NAMES_LIST.add("Larry Page"); + UPDATED_NAMES_LIST.add("Roger Federer"); + UPDATED_NAMES_LIST.add("Barak Obama"); + } + + @Mock private ContactsManager mMockManager; + @Mock private Context mContext; + + private ContactsContentObserver mObserver; + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + mObserver = new ContactsContentObserver(mMockManager, mContext); + } + + @After + public void tearDown() { + validateMockitoUsage(); + } + + @Test + public void testHaveContentsChanged_NoChange() { + when(mMockManager.getContactCount()).thenReturn(STALE_CONTACT_COUNT); + when(mMockManager.getContactCountAtLastRebuild()).thenReturn(STALE_CONTACT_COUNT); + when(mMockManager.getValidNames(eq(Contacts.CONTENT_URI))).thenReturn(STALE_NAMES_LIST); + when(mMockManager.getHashCodeAtLastRebuild()).thenReturn(STALE_NAMES_LIST.hashCode()); + assertFalse(mObserver.haveContentsChanged()); + } + @Test + public void testHaveContentsChanged_UpdatedCount() { + when(mMockManager.getContactCount()).thenReturn(UPDATED_CONTACT_COUNT); + when(mMockManager.getContactCountAtLastRebuild()).thenReturn(STALE_CONTACT_COUNT); + assertTrue(mObserver.haveContentsChanged()); + } + + @Test + public void testHaveContentsChanged_HashUpdate() { + when(mMockManager.getContactCount()).thenReturn(STALE_CONTACT_COUNT); + when(mMockManager.getContactCountAtLastRebuild()).thenReturn(STALE_CONTACT_COUNT); + when(mMockManager.getValidNames(eq(Contacts.CONTENT_URI))).thenReturn(UPDATED_NAMES_LIST); + when(mMockManager.getHashCodeAtLastRebuild()).thenReturn(STALE_NAMES_LIST.hashCode()); + assertTrue(mObserver.haveContentsChanged()); + } +} diff --git a/tests/src/com/android/inputmethod/latin/ContactsDictionaryUtilsTest.java b/tests/src/com/android/inputmethod/latin/ContactsDictionaryUtilsTest.java new file mode 100644 index 000000000..9b49f1abb --- /dev/null +++ b/tests/src/com/android/inputmethod/latin/ContactsDictionaryUtilsTest.java @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.inputmethod.latin; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import android.test.suitebuilder.annotation.SmallTest; + +import org.junit.Test; + +import java.util.Locale; + +/** + * Tests for {@link ContactsDictionaryUtils} + */ +@SmallTest +public class ContactsDictionaryUtilsTest { + + @Test + public void testGetWordEndPosition() { + final String testString1 = "Larry Page"; + assertEquals(5, ContactsDictionaryUtils.getWordEndPosition( + testString1, testString1.length(), 0 /* startIndex */)); + + assertEquals(10, ContactsDictionaryUtils.getWordEndPosition( + testString1, testString1.length(), 6 /* startIndex */)); + + final String testString2 = "Larry-Page"; + assertEquals(10, ContactsDictionaryUtils.getWordEndPosition( + testString2, testString1.length(), 0 /* startIndex */)); + + final String testString3 = "Larry'Page"; + assertEquals(10, ContactsDictionaryUtils.getWordEndPosition( + testString3, testString1.length(), 0 /* startIndex */)); + } + + @Test + public void testUseFirstLastBigramsForLocale() { + assertTrue(ContactsDictionaryUtils.useFirstLastBigramsForLocale(Locale.ENGLISH)); + assertTrue(ContactsDictionaryUtils.useFirstLastBigramsForLocale(Locale.US)); + assertTrue(ContactsDictionaryUtils.useFirstLastBigramsForLocale(Locale.UK)); + assertFalse(ContactsDictionaryUtils.useFirstLastBigramsForLocale(Locale.CHINA)); + assertFalse(ContactsDictionaryUtils.useFirstLastBigramsForLocale(Locale.GERMAN)); + } +} diff --git a/tests/src/com/android/inputmethod/latin/ContactsManagerTest.java b/tests/src/com/android/inputmethod/latin/ContactsManagerTest.java new file mode 100644 index 000000000..6326b3b0f --- /dev/null +++ b/tests/src/com/android/inputmethod/latin/ContactsManagerTest.java @@ -0,0 +1,127 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.inputmethod.latin; + +import android.content.ContentResolver; +import android.content.Context; +import android.database.Cursor; +import android.database.MatrixCursor; +import android.net.Uri; +import android.provider.ContactsContract; +import android.provider.ContactsContract.Contacts; +import android.test.AndroidTestCase; +import android.test.RenamingDelegatingContext; +import android.test.mock.MockContentProvider; +import android.test.mock.MockContentResolver; +import android.test.suitebuilder.annotation.SmallTest; + +import org.junit.Before; +import org.junit.Test; + +import java.util.ArrayList; +import java.util.HashMap; + +/** + * Tests for {@link ContactsManager} + */ +@SmallTest +public class ContactsManagerTest extends AndroidTestCase { + + private ContactsManager mManager; + private FakeContactsContentProvider mFakeContactsContentProvider; + private MatrixCursor mMatrixCursor; + + @Before + @Override + public void setUp() throws Exception { + // Fake content provider + mFakeContactsContentProvider = new FakeContactsContentProvider(); + mMatrixCursor = new MatrixCursor(ContactsDictionaryConstants.PROJECTION); + // Add the fake content provider to fake content resolver. + final MockContentResolver contentResolver = new MockContentResolver(); + contentResolver.addProvider(ContactsContract.AUTHORITY, mFakeContactsContentProvider); + // Add the fake content resolver to a fake context. + final ContextWithMockContentResolver context = new ContextWithMockContentResolver(mContext); + context.setContentResolver(contentResolver); + + mManager = new ContactsManager(context); + } + + @Test + public void testGetValidNames() { + final String contactName1 = "firstname lastname"; + final String contactName2 = "larry"; + mMatrixCursor.addRow(new Object[] { 1, contactName1 }); + mMatrixCursor.addRow(new Object[] { 2, null /* null name */ }); + mMatrixCursor.addRow(new Object[] { 3, contactName2 }); + mMatrixCursor.addRow(new Object[] { 4, "floopy@example.com" /* invalid name */ }); + mFakeContactsContentProvider.addQueryResult(Contacts.CONTENT_URI, mMatrixCursor); + + final ArrayList<String> validNames = mManager.getValidNames(Contacts.CONTENT_URI); + assertEquals(2, validNames.size()); + assertEquals(contactName1, validNames.get(0)); + assertEquals(contactName2, validNames.get(1)); + } + + @Test + public void testGetCount() { + mMatrixCursor.addRow(new Object[] { 1, "firstname" }); + mMatrixCursor.addRow(new Object[] { 2, null /* null name */ }); + mMatrixCursor.addRow(new Object[] { 3, "larry" }); + mMatrixCursor.addRow(new Object[] { 4, "floopy@example.com" /* invalid name */ }); + mFakeContactsContentProvider.addQueryResult(Contacts.CONTENT_URI, mMatrixCursor); + + assertEquals(4, mManager.getContactCount()); + } + + + static class ContextWithMockContentResolver extends RenamingDelegatingContext { + private ContentResolver contentResolver; + + public void setContentResolver(final ContentResolver contentResolver) { + this.contentResolver = contentResolver; + } + + public ContextWithMockContentResolver(final Context targetContext) { + super(targetContext, "test"); + } + + @Override + public ContentResolver getContentResolver() { + return contentResolver; + } + } + + static class FakeContactsContentProvider extends MockContentProvider { + private final HashMap<String, MatrixCursor> mQueryCursorMapForTestExpectations = + new HashMap<>(); + + @Override + public Cursor query(final Uri uri, final String[] projection, final String selection, + final String[] selectionArgs, final String sortOrder) { + return mQueryCursorMapForTestExpectations.get(uri.toString()); + } + + public void reset() { + mQueryCursorMapForTestExpectations.clear(); + } + + public void addQueryResult(final Uri uri, final MatrixCursor cursor) { + mQueryCursorMapForTestExpectations.put(uri.toString(), cursor); + } + } +} diff --git a/tests/src/com/android/inputmethod/latin/DictionaryFacilitatorLruCacheTests.java b/tests/src/com/android/inputmethod/latin/DictionaryFacilitatorLruCacheTests.java new file mode 100644 index 000000000..6b0bbc279 --- /dev/null +++ b/tests/src/com/android/inputmethod/latin/DictionaryFacilitatorLruCacheTests.java @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.inputmethod.latin; + +import java.util.Locale; + +import android.test.AndroidTestCase; +import android.test.suitebuilder.annotation.LargeTest; + +@LargeTest +public class DictionaryFacilitatorLruCacheTests extends AndroidTestCase { + public void testGetFacilitator() { + final DictionaryFacilitatorLruCache cache = + new DictionaryFacilitatorLruCache(getContext(), ""); + + final DictionaryFacilitator dictionaryFacilitatorEnUs = cache.get(Locale.US); + assertNotNull(dictionaryFacilitatorEnUs); + assertTrue(dictionaryFacilitatorEnUs.isForLocale(Locale.US)); + + final DictionaryFacilitator dictionaryFacilitatorFr = cache.get(Locale.FRENCH); + assertNotNull(dictionaryFacilitatorEnUs); + assertTrue(dictionaryFacilitatorFr.isForLocale(Locale.FRENCH)); + + final DictionaryFacilitator dictionaryFacilitatorDe = cache.get(Locale.GERMANY); + assertNotNull(dictionaryFacilitatorDe); + assertTrue(dictionaryFacilitatorDe.isForLocale(Locale.GERMANY)); + } +} diff --git a/tests/src/com/android/inputmethod/latin/DistracterFilterTest.java b/tests/src/com/android/inputmethod/latin/DistracterFilterTest.java deleted file mode 100644 index e6fb28260..000000000 --- a/tests/src/com/android/inputmethod/latin/DistracterFilterTest.java +++ /dev/null @@ -1,197 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.inputmethod.latin; - -import java.util.ArrayList; -import java.util.Locale; - -import android.content.Context; -import android.test.AndroidTestCase; -import android.test.suitebuilder.annotation.LargeTest; -import android.view.inputmethod.InputMethodSubtype; - -import com.android.inputmethod.latin.utils.DistracterFilterCheckingExactMatchesAndSuggestions; - -/** - * Unit test for DistracterFilter - */ -@LargeTest -public class DistracterFilterTest extends AndroidTestCase { - private DistracterFilterCheckingExactMatchesAndSuggestions mDistracterFilter; - - @Override - protected void setUp() throws Exception { - super.setUp(); - final Context context = getContext(); - mDistracterFilter = new DistracterFilterCheckingExactMatchesAndSuggestions(context); - RichInputMethodManager.init(context); - final RichInputMethodManager richImm = RichInputMethodManager.getInstance(); - final ArrayList<InputMethodSubtype> subtypes = new ArrayList<>(); - subtypes.add(richImm.findSubtypeByLocaleAndKeyboardLayoutSet( - Locale.US.toString(), "qwerty")); - subtypes.add(richImm.findSubtypeByLocaleAndKeyboardLayoutSet( - Locale.FRENCH.toString(), "azerty")); - subtypes.add(richImm.findSubtypeByLocaleAndKeyboardLayoutSet( - Locale.GERMAN.toString(), "qwertz")); - mDistracterFilter.updateEnabledSubtypes(subtypes); - } - - public void testIsDistractorToWordsInDictionaries() { - final PrevWordsInfo EMPTY_PREV_WORDS_INFO = PrevWordsInfo.EMPTY_PREV_WORDS_INFO; - - final Locale localeEnUs = new Locale("en", "US"); - String typedWord; - - typedWord = "Bill"; - // For this test case, we consider "Bill" is a distracter to "bill". - assertTrue(mDistracterFilter.isDistracterToWordsInDictionaries( - EMPTY_PREV_WORDS_INFO, typedWord, localeEnUs)); - - typedWord = "nOt"; - // For this test case, we consider "nOt" is a distracter to "not". - assertTrue(mDistracterFilter.isDistracterToWordsInDictionaries( - EMPTY_PREV_WORDS_INFO, typedWord, localeEnUs)); - - typedWord = "youre"; - // For this test case, we consider "youre" is a distracter to "you're". - assertTrue(mDistracterFilter.isDistracterToWordsInDictionaries( - EMPTY_PREV_WORDS_INFO, typedWord, localeEnUs)); - - typedWord = "Banana"; - // For this test case, we consider "Banana" is a distracter to "banana". - assertTrue(mDistracterFilter.isDistracterToWordsInDictionaries( - EMPTY_PREV_WORDS_INFO, typedWord, localeEnUs)); - - typedWord = "orange"; - // For this test case, we consider "orange" is not a distracter to any word in dictionaries. - assertFalse(mDistracterFilter.isDistracterToWordsInDictionaries( - EMPTY_PREV_WORDS_INFO, typedWord, localeEnUs)); - - typedWord = "Orange"; - // For this test case, we consider "Orange" is a distracter to "orange". - assertTrue(mDistracterFilter.isDistracterToWordsInDictionaries( - EMPTY_PREV_WORDS_INFO, typedWord, localeEnUs)); - - typedWord = "café"; - // For this test case, we consider "café" is a distracter to "cafe". - assertTrue(mDistracterFilter.isDistracterToWordsInDictionaries( - EMPTY_PREV_WORDS_INFO, typedWord, localeEnUs)); - - typedWord = "cafe"; - // For this test case, we consider "cafe" is not a distracter to any word in dictionaries. - assertFalse(mDistracterFilter.isDistracterToWordsInDictionaries( - EMPTY_PREV_WORDS_INFO, typedWord, localeEnUs)); - - typedWord = "I'll"; - // For this test case, we consider "I'll" is not a distracter to any word in dictionaries. - assertFalse(mDistracterFilter.isDistracterToWordsInDictionaries( - EMPTY_PREV_WORDS_INFO, typedWord, localeEnUs)); - - typedWord = "ill"; - // For this test case, we consider "ill" is a distracter to "I'll" - assertTrue(mDistracterFilter.isDistracterToWordsInDictionaries( - EMPTY_PREV_WORDS_INFO, typedWord, localeEnUs)); - - typedWord = "asdfd"; - // For this test case, we consider "asdfd" is not a distracter to any word in dictionaries. - assertFalse( - mDistracterFilter.isDistracterToWordsInDictionaries( - EMPTY_PREV_WORDS_INFO, typedWord, localeEnUs)); - - typedWord = "thank"; - // For this test case, we consider "thank" is not a distracter to any other word - // in dictionaries. - assertFalse(mDistracterFilter.isDistracterToWordsInDictionaries( - EMPTY_PREV_WORDS_INFO, typedWord, localeEnUs)); - - typedWord = "thabk"; - // For this test case, we consider "thabk" is a distracter to "thank" - assertTrue(mDistracterFilter.isDistracterToWordsInDictionaries( - EMPTY_PREV_WORDS_INFO, typedWord, localeEnUs)); - - typedWord = "thanks"; - // For this test case, we consider "thanks" is not a distracter to any other word - // in dictionaries. - assertFalse(mDistracterFilter.isDistracterToWordsInDictionaries( - EMPTY_PREV_WORDS_INFO, typedWord, localeEnUs)); - - typedWord = "thabks"; - // For this test case, we consider "thabks" is a distracter to "thanks" - assertTrue(mDistracterFilter.isDistracterToWordsInDictionaries( - EMPTY_PREV_WORDS_INFO, typedWord, localeEnUs)); - - typedWord = "think"; - // For this test case, we consider "think" is not a distracter to any other word - // in dictionaries. - assertFalse(mDistracterFilter.isDistracterToWordsInDictionaries( - EMPTY_PREV_WORDS_INFO, typedWord, localeEnUs)); - - typedWord = "thibk"; - // For this test case, we consider "thibk" is a distracter to "think" - assertTrue(mDistracterFilter.isDistracterToWordsInDictionaries( - EMPTY_PREV_WORDS_INFO, typedWord, localeEnUs)); - - typedWord = "tgis"; - // For this test case, we consider "tgis" is a distracter to "this" - assertTrue(mDistracterFilter.isDistracterToWordsInDictionaries( - EMPTY_PREV_WORDS_INFO, typedWord, localeEnUs)); - - final Locale localeDeDe = new Locale("de"); - - typedWord = "fUEr"; - // For this test case, we consider "fUEr" is a distracter to "für". - assertTrue(mDistracterFilter.isDistracterToWordsInDictionaries( - EMPTY_PREV_WORDS_INFO, typedWord, localeDeDe)); - - typedWord = "fuer"; - // For this test case, we consider "fuer" is a distracter to "für". - assertTrue(mDistracterFilter.isDistracterToWordsInDictionaries( - EMPTY_PREV_WORDS_INFO, typedWord, localeDeDe)); - - typedWord = "fur"; - // For this test case, we consider "fur" is a distracter to "für". - assertTrue(mDistracterFilter.isDistracterToWordsInDictionaries( - EMPTY_PREV_WORDS_INFO, typedWord, localeDeDe)); - - final Locale localeFrFr = new Locale("fr"); - - typedWord = "a"; - // For this test case, we consider "a" is a distracter to "à". - assertTrue(mDistracterFilter.isDistracterToWordsInDictionaries( - EMPTY_PREV_WORDS_INFO, typedWord, localeFrFr)); - - typedWord = "à"; - // For this test case, we consider "à" is not a distracter to any word in dictionaries. - assertFalse(mDistracterFilter.isDistracterToWordsInDictionaries( - EMPTY_PREV_WORDS_INFO, typedWord, localeFrFr)); - - typedWord = "etre"; - // For this test case, we consider "etre" is a distracter to "être". - assertTrue(mDistracterFilter.isDistracterToWordsInDictionaries( - EMPTY_PREV_WORDS_INFO, typedWord, localeFrFr)); - - typedWord = "États-unis"; - // For this test case, we consider "États-unis" is a distracter to "États-Unis". - assertTrue(mDistracterFilter.isDistracterToWordsInDictionaries( - EMPTY_PREV_WORDS_INFO, typedWord, localeFrFr)); - - typedWord = "ÉtatsUnis"; - // For this test case, we consider "ÉtatsUnis" is a distracter to "États-Unis". - assertTrue(mDistracterFilter.isDistracterToWordsInDictionaries( - EMPTY_PREV_WORDS_INFO, typedWord, localeFrFr)); - } -} diff --git a/tests/src/com/android/inputmethod/latin/FusionDictionaryTests.java b/tests/src/com/android/inputmethod/latin/FusionDictionaryTests.java deleted file mode 100644 index 09309bcc0..000000000 --- a/tests/src/com/android/inputmethod/latin/FusionDictionaryTests.java +++ /dev/null @@ -1,52 +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.test.AndroidTestCase; -import android.test.suitebuilder.annotation.SmallTest; - -import com.android.inputmethod.latin.makedict.FormatSpec.DictionaryOptions; -import com.android.inputmethod.latin.makedict.FusionDictionary; -import com.android.inputmethod.latin.makedict.ProbabilityInfo; -import com.android.inputmethod.latin.makedict.FusionDictionary.PtNodeArray; - -import java.util.HashMap; - -/** - * Unit test for FusionDictionary - */ -@SmallTest -public class FusionDictionaryTests extends AndroidTestCase { - public void testFindWordInTree() { - FusionDictionary dict = new FusionDictionary(new PtNodeArray(), - new DictionaryOptions(new HashMap<String,String>())); - - dict.add("abc", new ProbabilityInfo(10), null, false /* isNotAWord */); - assertNull(FusionDictionary.findWordInTree(dict.mRootNodeArray, "aaa")); - assertNotNull(FusionDictionary.findWordInTree(dict.mRootNodeArray, "abc")); - - dict.add("aa", new ProbabilityInfo(10), null, false /* isNotAWord */); - assertNull(FusionDictionary.findWordInTree(dict.mRootNodeArray, "aaa")); - assertNotNull(FusionDictionary.findWordInTree(dict.mRootNodeArray, "aa")); - - dict.add("babcd", new ProbabilityInfo(10), null, false /* isNotAWord */); - dict.add("bacde", new ProbabilityInfo(10), null, false /* isNotAWord */); - 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 59b858dbd..02cd21c8d 100644 --- a/tests/src/com/android/inputmethod/latin/InputLogicTests.java +++ b/tests/src/com/android/inputmethod/latin/InputLogicTests.java @@ -16,15 +16,33 @@ package com.android.inputmethod.latin; +import static android.test.MoreAsserts.assertNotEqual; + import android.test.suitebuilder.annotation.LargeTest; import android.text.TextUtils; import android.view.inputmethod.BaseInputConnection; +import com.android.inputmethod.latin.common.Constants; +import com.android.inputmethod.latin.define.DecoderSpecificConstants; import com.android.inputmethod.latin.settings.Settings; @LargeTest public class InputLogicTests extends InputTestsBase { + private boolean mNextWordPrediction; + + @Override + public void setUp() throws Exception { + super.setUp(); + mNextWordPrediction = getBooleanPreference(Settings.PREF_BIGRAM_PREDICTIONS, true); + } + + @Override + public void tearDown() throws Exception { + setBooleanPreference(Settings.PREF_BIGRAM_PREDICTIONS, mNextWordPrediction, true); + super.tearDown(); + } + public void testTypeWord() { final String WORD_TO_TYPE = "abcd"; type(WORD_TO_TYPE); @@ -36,7 +54,7 @@ public class InputLogicTests extends InputTestsBase { final String EXPECTED_RESULT = "thi"; type(WORD_TO_TYPE); pickSuggestionManually(WORD_TO_TYPE); - mLatinIME.onUpdateSelection(0, 0, WORD_TO_TYPE.length(), WORD_TO_TYPE.length(), -1, -1); + sendUpdateForCursorMoveTo(WORD_TO_TYPE.length()); type(Constants.CODE_DELETE); assertEquals("press suggestion then backspace", EXPECTED_RESULT, mEditText.getText().toString()); @@ -49,7 +67,7 @@ public class InputLogicTests extends InputTestsBase { type(WORD_TO_TYPE); // Choose the auto-correction. For "tgis", the auto-correction should be "this". pickSuggestionManually(WORD_TO_PICK); - mLatinIME.onUpdateSelection(0, 0, WORD_TO_TYPE.length(), WORD_TO_TYPE.length(), -1, -1); + sendUpdateForCursorMoveTo(WORD_TO_TYPE.length()); assertEquals("pick typed word over auto-correction then backspace", WORD_TO_PICK, mEditText.getText().toString()); type(Constants.CODE_DELETE); @@ -63,7 +81,7 @@ public class InputLogicTests extends InputTestsBase { type(WORD_TO_TYPE); // Choose the typed word. pickSuggestionManually(WORD_TO_TYPE); - mLatinIME.onUpdateSelection(0, 0, WORD_TO_TYPE.length(), WORD_TO_TYPE.length(), -1, -1); + sendUpdateForCursorMoveTo(WORD_TO_TYPE.length()); assertEquals("pick typed word over auto-correction then backspace", WORD_TO_TYPE, mEditText.getText().toString()); type(Constants.CODE_DELETE); @@ -78,7 +96,7 @@ public class InputLogicTests extends InputTestsBase { type(WORD_TO_TYPE); // Choose the second suggestion, which should be "thus" when "tgis" is typed. pickSuggestionManually(WORD_TO_PICK); - mLatinIME.onUpdateSelection(0, 0, WORD_TO_TYPE.length(), WORD_TO_TYPE.length(), -1, -1); + sendUpdateForCursorMoveTo(WORD_TO_TYPE.length()); assertEquals("pick different suggestion then backspace", WORD_TO_PICK, mEditText.getText().toString()); type(Constants.CODE_DELETE); @@ -93,7 +111,8 @@ public class InputLogicTests extends InputTestsBase { final int SELECTION_END = 19; final String EXPECTED_RESULT = "some text some text"; type(STRING_TO_TYPE); - // There is no IMF to call onUpdateSelection for us so we must do it by hand. + // Don't use the sendUpdateForCursorMove* family of methods here because they + // don't handle selections. // Send once to simulate the cursor actually responding to the move caused by typing. // This is necessary because LatinIME is bookkeeping to avoid confusing a real cursor // move with a move triggered by LatinIME inputting stuff. @@ -113,7 +132,8 @@ public class InputLogicTests extends InputTestsBase { final int SELECTION_END = 19; final String EXPECTED_RESULT = "some text some text"; type(STRING_TO_TYPE); - // There is no IMF to call onUpdateSelection for us so we must do it by hand. + // Don't use the sendUpdateForCursorMove* family of methods here because they + // don't handle selections. // Send once to simulate the cursor actually responding to the move caused by typing. // This is necessary because LatinIME is bookkeeping to avoid confusing a real cursor // move with a move triggered by LatinIME inputting stuff. @@ -152,49 +172,90 @@ public class InputLogicTests extends InputTestsBase { final String STRING_TO_TYPE = "tgis."; final String EXPECTED_RESULT = "tgis."; type(STRING_TO_TYPE); - mLatinIME.onUpdateSelection(0, 0, STRING_TO_TYPE.length(), STRING_TO_TYPE.length(), -1, -1); + sendUpdateForCursorMoveTo(STRING_TO_TYPE.length()); type(Constants.CODE_DELETE); assertEquals("auto-correct with period then revert", EXPECTED_RESULT, mEditText.getText().toString()); } public void testAutoCorrectWithSpaceThenRevert() { + // Backspacing to cancel the "tgis"->"this" autocorrection should result in + // a "phantom space": if the user presses space immediately after, + // only one space will be inserted in total. final String STRING_TO_TYPE = "tgis "; - final String EXPECTED_RESULT = "tgis "; + final String EXPECTED_RESULT = "tgis"; type(STRING_TO_TYPE); - mLatinIME.onUpdateSelection(0, 0, STRING_TO_TYPE.length(), STRING_TO_TYPE.length(), -1, -1); + sendUpdateForCursorMoveTo(STRING_TO_TYPE.length()); type(Constants.CODE_DELETE); assertEquals("auto-correct with space then revert", EXPECTED_RESULT, mEditText.getText().toString()); } + public void testAutoCorrectWithSpaceThenRevertThenTypeMore() { + final String STRING_TO_TYPE_FIRST = "tgis "; + final String STRING_TO_TYPE_SECOND = "a"; + final String EXPECTED_RESULT = "tgis a"; + type(STRING_TO_TYPE_FIRST); + sendUpdateForCursorMoveTo(STRING_TO_TYPE_FIRST.length()); + type(Constants.CODE_DELETE); + + type(STRING_TO_TYPE_SECOND); + sendUpdateForCursorMoveTo(STRING_TO_TYPE_FIRST.length() - 1 + + STRING_TO_TYPE_SECOND.length()); + assertEquals("auto-correct with space then revert then type more", EXPECTED_RESULT, + mEditText.getText().toString()); + } + public void testAutoCorrectToSelfDoesNotRevert() { final String STRING_TO_TYPE = "this "; final String EXPECTED_RESULT = "this"; type(STRING_TO_TYPE); - mLatinIME.onUpdateSelection(0, 0, STRING_TO_TYPE.length(), STRING_TO_TYPE.length(), -1, -1); + sendUpdateForCursorMoveTo(STRING_TO_TYPE.length()); type(Constants.CODE_DELETE); assertEquals("auto-correct with space does not revert", EXPECTED_RESULT, mEditText.getText().toString()); } public void testDoubleSpace() { - // Set default pref just in case - setBooleanPreference(Settings.PREF_KEY_USE_DOUBLE_SPACE_PERIOD, true, true); // U+1F607 is an emoji final String[] STRINGS_TO_TYPE = new String[] { "this ", "a+ ", "\u1F607 ", ".. ", ") ", "( ", "% " }; final String[] EXPECTED_RESULTS = new String[] { "this. ", "a+. ", "\u1F607. ", ".. ", "). ", "( ", "%. " }; - for (int i = 0; i < STRINGS_TO_TYPE.length; ++i) { + verifyDoubleSpace(STRINGS_TO_TYPE, EXPECTED_RESULTS); + } + + public void testDoubleSpaceHindi() { + changeLanguage("hi"); + // U+1F607 is an emoji + final String[] STRINGS_TO_TYPE = + new String[] { "this ", "a+ ", "\u1F607 ", "|| ", ") ", "( ", "% " }; + final String[] EXPECTED_RESULTS = + new String[] { "this| ", "a+| ", "\u1F607| ", "|| ", ")| ", "( ", "%| " }; + verifyDoubleSpace(STRINGS_TO_TYPE, EXPECTED_RESULTS); + } + + private void verifyDoubleSpace(String[] stringsToType, String[] expectedResults) { + // Set default pref just in case + setBooleanPreference(Settings.PREF_KEY_USE_DOUBLE_SPACE_PERIOD, true, true); + for (int i = 0; i < stringsToType.length; ++i) { mEditText.setText(""); - type(STRINGS_TO_TYPE[i]); - assertEquals("double space processing", EXPECTED_RESULTS[i], + type(stringsToType[i]); + assertEquals("double space processing", expectedResults[i], mEditText.getText().toString()); } } - public void testCancelDoubleSpace() { + public void testCancelDoubleSpaceEnglish() { + final String STRING_TO_TYPE = "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()); + } + + public void testCancelDoubleSpaceHindi() { + changeLanguage("hi"); final String STRING_TO_TYPE = "this "; final String EXPECTED_RESULT = "this "; type(STRING_TO_TYPE); @@ -243,9 +304,9 @@ public class InputLogicTests extends InputTestsBase { public void testDoubleSpacePeriod() { // Reset settings to default, else these tests will go flaky. setBooleanPreference(Settings.PREF_SHOW_SUGGESTIONS, true, true); - setStringPreference(Settings.PREF_AUTO_CORRECTION_THRESHOLD, "1", "1"); + setBooleanPreference(Settings.PREF_AUTO_CORRECTION, true, true); setBooleanPreference(Settings.PREF_KEY_USE_DOUBLE_SPACE_PERIOD, true, true); - testDoubleSpacePeriodWithSettings(true /* expectsPeriod */); + testDoubleSpacePeriodWithSettings(true); // "Suggestion visibility" to off testDoubleSpacePeriodWithSettings(true, Settings.PREF_SHOW_SUGGESTIONS, false); // "Suggestion visibility" to on @@ -255,18 +316,16 @@ public class InputLogicTests extends InputTestsBase { testDoubleSpacePeriodWithSettings(false, Settings.PREF_KEY_USE_DOUBLE_SPACE_PERIOD, false); // "Auto-correction" to "off" - testDoubleSpacePeriodWithSettings(true, Settings.PREF_AUTO_CORRECTION_THRESHOLD, "0"); - // "Auto-correction" to "modest" - testDoubleSpacePeriodWithSettings(true, Settings.PREF_AUTO_CORRECTION_THRESHOLD, "1"); - // "Auto-correction" to "very aggressive" - testDoubleSpacePeriodWithSettings(true, Settings.PREF_AUTO_CORRECTION_THRESHOLD, "3"); + testDoubleSpacePeriodWithSettings(true, Settings.PREF_AUTO_CORRECTION, false); + // "Auto-correction" to "on" + testDoubleSpacePeriodWithSettings(true, Settings.PREF_AUTO_CORRECTION, true); // "Suggestion visibility" to "always hide" and "Auto-correction" to "off" testDoubleSpacePeriodWithSettings(true, Settings.PREF_SHOW_SUGGESTIONS, false, - Settings.PREF_AUTO_CORRECTION_THRESHOLD, "0"); + Settings.PREF_AUTO_CORRECTION, false); // "Suggestion visibility" to "always hide" and "Auto-correction" to "off" testDoubleSpacePeriodWithSettings(false, Settings.PREF_SHOW_SUGGESTIONS, false, - Settings.PREF_AUTO_CORRECTION_THRESHOLD, "0", + Settings.PREF_AUTO_CORRECTION, false, Settings.PREF_KEY_USE_DOUBLE_SPACE_PERIOD, false); } @@ -276,10 +335,9 @@ public class InputLogicTests extends InputTestsBase { final String EXPECTED_RESULT = "this "; final int NEW_CURSOR_POSITION = 0; type(STRING_TO_TYPE); - mLatinIME.onUpdateSelection(0, 0, typedLength, typedLength, -1, -1); + sendUpdateForCursorMoveTo(typedLength); mInputConnection.setSelection(NEW_CURSOR_POSITION, NEW_CURSOR_POSITION); - mLatinIME.onUpdateSelection(typedLength, typedLength, - NEW_CURSOR_POSITION, NEW_CURSOR_POSITION, -1, -1); + sendUpdateForCursorMoveTo(NEW_CURSOR_POSITION); type(Constants.CODE_DELETE); assertEquals("auto correct then move cursor to start of line then backspace", EXPECTED_RESULT, mEditText.getText().toString()); @@ -291,10 +349,9 @@ public class InputLogicTests extends InputTestsBase { final String EXPECTED_RESULT = "andthis "; final int NEW_CURSOR_POSITION = STRING_TO_TYPE.indexOf('t'); type(STRING_TO_TYPE); - mLatinIME.onUpdateSelection(0, 0, typedLength, typedLength, -1, -1); + sendUpdateForCursorMoveTo(typedLength); mInputConnection.setSelection(NEW_CURSOR_POSITION, NEW_CURSOR_POSITION); - mLatinIME.onUpdateSelection(typedLength, typedLength, - NEW_CURSOR_POSITION, NEW_CURSOR_POSITION, -1, -1); + sendUpdateForCursorMoveTo(NEW_CURSOR_POSITION); type(Constants.CODE_DELETE); assertEquals("auto correct then move cursor then backspace", EXPECTED_RESULT, mEditText.getText().toString()); @@ -329,8 +386,8 @@ public class InputLogicTests extends InputTestsBase { assertEquals("manual pick then separator", EXPECTED_RESULT, mEditText.getText().toString()); } - // This test matches the one in InputLogicTestsNonEnglish. In some non-English languages, - // ! and ? are clustering punctuation signs. + // This test matches testClusteringPunctuationForFrench. + // In some non-English languages, ! and ? are clustering punctuation signs. public void testClusteringPunctuation() { final String WORD1_TO_TYPE = "test"; final String WORD2_TO_TYPE = "!!?!:!"; @@ -394,7 +451,7 @@ public class InputLogicTests extends InputTestsBase { BaseInputConnection.getComposingSpanStart(mEditText.getText())); assertEquals("resume suggestion on backspace", -1, BaseInputConnection.getComposingSpanEnd(mEditText.getText())); - mLatinIME.onUpdateSelection(0, 0, typedLength, typedLength, -1, -1); + sendUpdateForCursorMoveTo(typedLength); type(Constants.CODE_DELETE); assertEquals("resume suggestion on backspace", 4, BaseInputConnection.getComposingSpanStart(mEditText.getText())); @@ -429,44 +486,25 @@ public class InputLogicTests extends InputTestsBase { type(" "); helperTestComposing("a'", true); } + // TODO: Add some tests for non-BMP characters public void testAutoCorrectByUserHistory() { - final String WORD_TO_BE_CORRECTED = "qpmx"; - final String NOT_CORRECTED_RESULT = "qpmx "; - final String DESIRED_WORD = "qpmz"; - final String CORRECTED_RESULT = "qpmz "; - final int typeCountNotToAutocorrect = 1; - final int typeCountToAutoCorrect = 16; - int startIndex = 0; - int endIndex = 0; - - for (int i = 0; i < typeCountNotToAutocorrect; i++) { - type(DESIRED_WORD); - type(Constants.CODE_SPACE); - } - startIndex = mEditText.getText().length(); - type(WORD_TO_BE_CORRECTED); + type("qpmz"); type(Constants.CODE_SPACE); - endIndex = mEditText.getText().length(); - assertEquals("not auto-corrected by user history", NOT_CORRECTED_RESULT, - mEditText.getText().subSequence(startIndex, endIndex).toString()); - for (int i = typeCountNotToAutocorrect; i < typeCountToAutoCorrect; i++) { - type(DESIRED_WORD); - type(Constants.CODE_SPACE); - } - startIndex = mEditText.getText().length(); - type(WORD_TO_BE_CORRECTED); + + int startIndex = mEditText.getText().length(); + type("qpmx"); type(Constants.CODE_SPACE); - endIndex = mEditText.getText().length(); + int endIndex = mEditText.getText().length(); assertEquals("auto-corrected by user history", - CORRECTED_RESULT, mEditText.getText().subSequence(startIndex, endIndex).toString()); + "qpmz ", mEditText.getText().subSequence(startIndex, endIndex).toString()); } public void testPredictionsAfterSpace() { final String WORD_TO_TYPE = "Barack "; type(WORD_TO_TYPE); - sleep(DELAY_TO_WAIT_FOR_PREDICTIONS); + sleep(DELAY_TO_WAIT_FOR_PREDICTIONS_MILLIS); runMessages(); // Test the first prediction is displayed final SuggestedWords suggestedWords = mLatinIME.getSuggestedWordsForTest(); @@ -476,24 +514,19 @@ public class InputLogicTests extends InputTestsBase { public void testPredictionsWithDoubleSpaceToPeriod() { mLatinIME.clearPersonalizedDictionariesForTest(); - final String WORD_TO_TYPE = "Barack "; + final String WORD_TO_TYPE = "Barack "; type(WORD_TO_TYPE); - sleep(DELAY_TO_WAIT_FOR_PREDICTIONS); + sleep(DELAY_TO_WAIT_FOR_PREDICTIONS_MILLIS); runMessages(); - // No need to test here, testPredictionsAfterSpace is testing it already - type(" "); - sleep(DELAY_TO_WAIT_FOR_PREDICTIONS); - runMessages(); - // Test the predictions have been cleared - SuggestedWords suggestedWords = mLatinIME.getSuggestedWordsForTest(); - assertEquals("predictions cleared after double-space-to-period", suggestedWords.size(), 0); + type(Constants.CODE_DELETE); - sleep(DELAY_TO_WAIT_FOR_PREDICTIONS); + sleep(DELAY_TO_WAIT_FOR_PREDICTIONS_MILLIS); runMessages(); - // Test the first prediction is displayed + + SuggestedWords suggestedWords = mLatinIME.getSuggestedWordsForTest(); suggestedWords = mLatinIME.getSuggestedWordsForTest(); assertEquals("predictions after cancel double-space-to-period", "Obama", - suggestedWords.size() > 0 ? suggestedWords.getWord(0) : null); + mLatinIME.getSuggestedWordsForTest().getWord(0)); } public void testPredictionsAfterManualPick() { @@ -501,7 +534,7 @@ public class InputLogicTests extends InputTestsBase { type(WORD_TO_TYPE); // Choose the auto-correction. For "Barack", the auto-correction should be "Barack". pickSuggestionManually(WORD_TO_TYPE); - sleep(DELAY_TO_WAIT_FOR_PREDICTIONS); + sleep(DELAY_TO_WAIT_FOR_PREDICTIONS_MILLIS); runMessages(); // Test the first prediction is displayed final SuggestedWords suggestedWords = mLatinIME.getSuggestedWordsForTest(); @@ -513,17 +546,11 @@ public class InputLogicTests extends InputTestsBase { mLatinIME.clearPersonalizedDictionariesForTest(); final String WORD_TO_TYPE = "Barack. "; type(WORD_TO_TYPE); - sleep(DELAY_TO_WAIT_FOR_PREDICTIONS); + sleep(DELAY_TO_WAIT_FOR_PREDICTIONS_MILLIS); runMessages(); - SuggestedWords suggestedWords = mLatinIME.getSuggestedWordsForTest(); - assertEquals("No prediction after period after inputting once.", 0, suggestedWords.size()); - type(WORD_TO_TYPE); - sleep(DELAY_TO_WAIT_FOR_PREDICTIONS); - runMessages(); - suggestedWords = mLatinIME.getSuggestedWordsForTest(); - assertEquals("Beginning-of-Sentence prediction after inputting 2 times.", "Barack", - suggestedWords.size() > 0 ? suggestedWords.getWord(0) : null); + SuggestedWords suggestedWords = mLatinIME.getSuggestedWordsForTest(); + assertFalse(mLatinIME.getSuggestedWordsForTest().isEmpty()); } public void testPredictionsAfterRecorrection() { @@ -535,27 +562,23 @@ public class InputLogicTests extends InputTestsBase { final int endOfSuggestion = endOfPrefix + FIRST_NON_TYPED_SUGGESTION.length(); final int indexForManualCursor = endOfPrefix + 3; // +3 because it's after "Bar" in "Barack" type(PREFIX); - mLatinIME.onUpdateSelection(0, 0, endOfPrefix, endOfPrefix, -1, -1); + sendUpdateForCursorMoveTo(endOfPrefix); type(WORD_TO_TYPE); pickSuggestionManually(FIRST_NON_TYPED_SUGGESTION); - mLatinIME.onUpdateSelection(endOfPrefix, endOfPrefix, endOfSuggestion, endOfSuggestion, - -1, -1); + sendUpdateForCursorMoveTo(endOfSuggestion); runMessages(); type(" "); - mLatinIME.onUpdateSelection(endOfSuggestion, endOfSuggestion, - endOfSuggestion + 1, endOfSuggestion + 1, -1, -1); - sleep(DELAY_TO_WAIT_FOR_PREDICTIONS); + sendUpdateForCursorMoveBy(1); + sleep(DELAY_TO_WAIT_FOR_PREDICTIONS_MILLIS); runMessages(); // Simulate a manual cursor move mInputConnection.setSelection(indexForManualCursor, indexForManualCursor); - mLatinIME.onUpdateSelection(endOfSuggestion + 1, endOfSuggestion + 1, - indexForManualCursor, indexForManualCursor, -1, -1); - sleep(DELAY_TO_WAIT_FOR_PREDICTIONS); + sendUpdateForCursorMoveTo(indexForManualCursor); + sleep(DELAY_TO_WAIT_FOR_PREDICTIONS_MILLIS); runMessages(); pickSuggestionManually(WORD_TO_TYPE); - mLatinIME.onUpdateSelection(indexForManualCursor, indexForManualCursor, - endOfWord, endOfWord, -1, -1); - sleep(DELAY_TO_WAIT_FOR_PREDICTIONS); + sendUpdateForCursorMoveTo(endOfWord); + sleep(DELAY_TO_WAIT_FOR_PREDICTIONS_MILLIS); runMessages(); // Test the first prediction is displayed final SuggestedWords suggestedWords = mLatinIME.getSuggestedWordsForTest(); @@ -603,7 +626,7 @@ public class InputLogicTests extends InputTestsBase { for (int i = 0; i < WORD_TO_TYPE.length(); ++i) { type(WORD_TO_TYPE.substring(i, i+1)); - sleep(DELAY_TO_WAIT_FOR_PREDICTIONS); + sleep(DELAY_TO_WAIT_FOR_PREDICTIONS_MILLIS); runMessages(); } assertEquals("type many trailing single quotes one by one", EXPECTED_RESULT, @@ -615,26 +638,148 @@ public class InputLogicTests extends InputTestsBase { final String EXPECTED_RESULT = WORD_TO_TYPE; for (int i = 0; i < WORD_TO_TYPE.length(); ++i) { type(WORD_TO_TYPE.substring(i, i+1)); - sleep(DELAY_TO_WAIT_FOR_PREDICTIONS); + sleep(DELAY_TO_WAIT_FOR_PREDICTIONS_MILLIS); runMessages(); } assertEquals("type words letter by letter", EXPECTED_RESULT, mEditText.getText().toString()); } - public void testSwitchLanguages() { - final String WORD_TO_TYPE_FIRST_PART = "com"; - final String WORD_TO_TYPE_SECOND_PART = "md"; - final String EXPECTED_RESULT = "comme"; - changeLanguage("en"); - type(WORD_TO_TYPE_FIRST_PART); + public void testBasicGesture() { + gesture("this"); + assertEquals("this", mEditText.getText().toString()); + } + + public void testGestureGesture() { + gesture("got"); + gesture("milk"); + assertEquals("got milk", mEditText.getText().toString()); + } + + public void testGestureBackspaceGestureAgain() { + gesture("this"); + type(Constants.CODE_DELETE); + assertEquals("gesture then backspace", "", mEditText.getText().toString()); + gesture("this"); + if (DecoderSpecificConstants.SHOULD_REMOVE_PREVIOUSLY_REJECTED_SUGGESTION) { + assertNotEqual("this", mEditText.getText().toString()); + } else { + assertEquals("this", mEditText.getText().toString()); + } + } + + private void typeOrGestureWordAndPutCursorInside(final boolean gesture, final String word, + final int startPos) { + final int END_OF_WORD = startPos + word.length(); + final int NEW_CURSOR_POSITION = startPos + word.length() / 2; + if (gesture) { + gesture(word); + } else { + type(word); + } + sendUpdateForCursorMoveTo(END_OF_WORD); + runMessages(); + sendUpdateForCursorMoveTo(NEW_CURSOR_POSITION); + sleep(DELAY_TO_WAIT_FOR_UNDERLINE_MILLIS); + runMessages(); + } + + private void typeWordAndPutCursorInside(final String word, final int startPos) { + typeOrGestureWordAndPutCursorInside(false /* gesture */, word, startPos); + } + + private void gestureWordAndPutCursorInside(final String word, final int startPos) { + typeOrGestureWordAndPutCursorInside(true /* gesture */, word, startPos); + } + + private void ensureComposingSpanPos(final String message, final int from, final int to) { + assertEquals(message, from, BaseInputConnection.getComposingSpanStart(mEditText.getText())); + assertEquals(message, to, BaseInputConnection.getComposingSpanEnd(mEditText.getText())); + } + + public void testTypeWithinComposing() { + final String WORD_TO_TYPE = "something"; + final String EXPECTED_RESULT = "some thing"; + typeWordAndPutCursorInside(WORD_TO_TYPE, 0 /* startPos */); + type(" "); + ensureComposingSpanPos("space while in the middle of a word cancels composition", -1, -1); + assertEquals("space in the middle of a composing word", EXPECTED_RESULT, + mEditText.getText().toString()); + int cursorPos = sendUpdateForCursorMoveToEndOfLine(); + runMessages(); + type(" "); + assertEquals("mbo", "some thing ", mEditText.getText().toString()); + typeWordAndPutCursorInside(WORD_TO_TYPE, cursorPos + 1 /* startPos */); + type(Constants.CODE_DELETE); + ensureComposingSpanPos("delete while in the middle of a word cancels composition", -1, -1); + } + + public void testTypeWithinGestureComposing() { + final String WORD_TO_TYPE = "something"; + final String EXPECTED_RESULT = "some thing"; + gestureWordAndPutCursorInside(WORD_TO_TYPE, 0 /* startPos */); + type(" "); + ensureComposingSpanPos("space while in the middle of a word cancels composition", -1, -1); + assertEquals("space in the middle of a composing word", EXPECTED_RESULT, + mEditText.getText().toString()); + int cursorPos = sendUpdateForCursorMoveToEndOfLine(); + runMessages(); + type(" "); + typeWordAndPutCursorInside(WORD_TO_TYPE, cursorPos + 1 /* startPos */); + type(Constants.CODE_DELETE); + sleep(DELAY_TO_WAIT_FOR_UNDERLINE_MILLIS); + ensureComposingSpanPos("delete while in the middle of a word cancels composition", -1, -1); + } + + public void testManualPickThenSeparatorForFrench() { + final String WORD1_TO_TYPE = "test"; + final String WORD2_TO_TYPE = "!"; + final String EXPECTED_RESULT = "test !"; changeLanguage("fr"); + type(WORD1_TO_TYPE); + pickSuggestionManually(WORD1_TO_TYPE); + type(WORD2_TO_TYPE); + assertEquals("manual pick then separator for French", EXPECTED_RESULT, + mEditText.getText().toString()); + } + + public void testClusteringPunctuationForFrench() { + final String WORD1_TO_TYPE = "test"; + final String WORD2_TO_TYPE = "!!?!:!"; + // In English, the expected result would be "test!!?!:!" + final String EXPECTED_RESULT = "test !!?! : !"; + changeLanguage("fr"); + type(WORD1_TO_TYPE); + pickSuggestionManually(WORD1_TO_TYPE); + type(WORD2_TO_TYPE); + assertEquals("clustering punctuation for French", EXPECTED_RESULT, + mEditText.getText().toString()); + } + + public void testWordThenSpaceThenPunctuationFromStripTwice() { + setBooleanPreference(Settings.PREF_BIGRAM_PREDICTIONS, false, true); + + final String WORD_TO_TYPE = "test "; + final String PUNCTUATION_FROM_STRIP = "!"; + final String EXPECTED_RESULT = "test!! "; + type(WORD_TO_TYPE); + sleep(DELAY_TO_WAIT_FOR_UNDERLINE_MILLIS); runMessages(); - type(WORD_TO_TYPE_SECOND_PART); - sleep(DELAY_TO_WAIT_FOR_UNDERLINE); + assertTrue("type word then type space should display punctuation strip", + mLatinIME.getSuggestedWordsForTest().isPunctuationSuggestions()); + pickSuggestionManually(PUNCTUATION_FROM_STRIP); + pickSuggestionManually(PUNCTUATION_FROM_STRIP); + assertEquals(EXPECTED_RESULT, mEditText.getText().toString()); + } + + public void testWordThenSpaceDisplaysPredictions() { + final String WORD_TO_TYPE = "Barack "; + final String EXPECTED_RESULT = "Obama"; + type(WORD_TO_TYPE); + sleep(DELAY_TO_WAIT_FOR_UNDERLINE_MILLIS); runMessages(); final SuggestedWords suggestedWords = mLatinIME.getSuggestedWordsForTest(); - assertEquals("Suggestions updated after switching languages", - EXPECTED_RESULT, suggestedWords.size() > 0 ? suggestedWords.getWord(1) : null); + assertEquals("type word then type space yields predictions for French", + EXPECTED_RESULT, suggestedWords.size() > 0 ? suggestedWords.getWord(0) : null); } } diff --git a/tests/src/com/android/inputmethod/latin/InputLogicTestsDeadKeys.java b/tests/src/com/android/inputmethod/latin/InputLogicTestsDeadKeys.java new file mode 100644 index 000000000..4b44138a7 --- /dev/null +++ b/tests/src/com/android/inputmethod/latin/InputLogicTestsDeadKeys.java @@ -0,0 +1,216 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.inputmethod.latin; + +import android.test.suitebuilder.annotation.LargeTest; + +import com.android.inputmethod.event.Event; +import com.android.inputmethod.latin.common.Constants; + +import java.util.ArrayList; + +@LargeTest +public class InputLogicTestsDeadKeys extends InputTestsBase { + // A helper class for readability + static class EventList extends ArrayList<Event> { + public EventList addCodePoint(final int codePoint, final boolean isDead) { + final Event event; + if (isDead) { + event = Event.createDeadEvent(codePoint, Event.NOT_A_KEY_CODE, null /* next */); + } else { + event = Event.createSoftwareKeypressEvent(codePoint, Event.NOT_A_KEY_CODE, + Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE, + false /* isKeyRepeat */); + } + add(event); + return this; + } + + public EventList addKey(final int keyCode) { + add(Event.createSoftwareKeypressEvent(Event.NOT_A_CODE_POINT, keyCode, + Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE, + false /* isKeyRepeat */)); + return this; + } + } + + public void testDeadCircumflexSimple() { + final int MODIFIER_LETTER_CIRCUMFLEX_ACCENT = 0x02C6; + final String EXPECTED_RESULT = "aê"; + final EventList events = new EventList() + .addCodePoint('a', false) + .addCodePoint(MODIFIER_LETTER_CIRCUMFLEX_ACCENT, true) + .addCodePoint('e', false); + for (final Event event : events) { + mLatinIME.onEvent(event); + } + assertEquals("simple dead circumflex", EXPECTED_RESULT, mEditText.getText().toString()); + } + + public void testDeadCircumflexBackspace() { + final int MODIFIER_LETTER_CIRCUMFLEX_ACCENT = 0x02C6; + final String EXPECTED_RESULT = "ae"; + final EventList events = new EventList() + .addCodePoint('a', false) + .addCodePoint(MODIFIER_LETTER_CIRCUMFLEX_ACCENT, true) + .addKey(Constants.CODE_DELETE) + .addCodePoint('e', false); + for (final Event event : events) { + mLatinIME.onEvent(event); + } + assertEquals("dead circumflex backspace", EXPECTED_RESULT, mEditText.getText().toString()); + } + + public void testDeadCircumflexFeedback() { + final int MODIFIER_LETTER_CIRCUMFLEX_ACCENT = 0x02C6; + final String EXPECTED_RESULT = "a\u02C6"; + final EventList events = new EventList() + .addCodePoint('a', false) + .addCodePoint(MODIFIER_LETTER_CIRCUMFLEX_ACCENT, true); + for (final Event event : events) { + mLatinIME.onEvent(event); + } + assertEquals("dead circumflex gives feedback", EXPECTED_RESULT, + mEditText.getText().toString()); + } + + public void testDeadDiaeresisSpace() { + final int MODIFIER_LETTER_DIAERESIS = 0xA8; + final String EXPECTED_RESULT = "a\u00A8e\u00A8i"; + final EventList events = new EventList() + .addCodePoint('a', false) + .addCodePoint(MODIFIER_LETTER_DIAERESIS, true) + .addCodePoint(Constants.CODE_SPACE, false) + .addCodePoint('e', false) + .addCodePoint(MODIFIER_LETTER_DIAERESIS, true) + .addCodePoint(Constants.CODE_ENTER, false) + .addCodePoint('i', false); + for (final Event event : events) { + mLatinIME.onEvent(event); + } + assertEquals("dead diaeresis space commits the dead char", EXPECTED_RESULT, + mEditText.getText().toString()); + } + + public void testDeadAcuteLetterBackspace() { + final int MODIFIER_LETTER_ACUTE = 0xB4; + final String EXPECTED_RESULT1 = "aá"; + final String EXPECTED_RESULT2 = "a"; + final EventList events = new EventList() + .addCodePoint('a', false) + .addCodePoint(MODIFIER_LETTER_ACUTE, true) + .addCodePoint('a', false); + for (final Event event : events) { + mLatinIME.onEvent(event); + } + assertEquals("dead acute on a typed", EXPECTED_RESULT1, mEditText.getText().toString()); + mLatinIME.onEvent(Event.createSoftwareKeypressEvent(Event.NOT_A_CODE_POINT, + Constants.CODE_DELETE, Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE, + false /* isKeyRepeat */)); + assertEquals("a with acute deleted", EXPECTED_RESULT2, mEditText.getText().toString()); + } + + public void testFinnishStroke() { + final int MODIFIER_LETTER_STROKE = '-'; + final String EXPECTED_RESULT = "x\u0110\u0127"; + final EventList events = new EventList() + .addCodePoint('x', false) + .addCodePoint(MODIFIER_LETTER_STROKE, true) + .addCodePoint('D', false) + .addCodePoint(MODIFIER_LETTER_STROKE, true) + .addCodePoint('h', false); + for (final Event event : events) { + mLatinIME.onEvent(event); + } + assertEquals("Finnish dead stroke", EXPECTED_RESULT, + mEditText.getText().toString()); + } + + public void testDoubleDeadOgonek() { + final int MODIFIER_LETTER_OGONEK = 0x02DB; + final String EXPECTED_RESULT = "txǫs\u02DBfk"; + final EventList events = new EventList() + .addCodePoint('t', false) + .addCodePoint('x', false) + .addCodePoint(MODIFIER_LETTER_OGONEK, true) + .addCodePoint('o', false) + .addCodePoint('s', false) + .addCodePoint(MODIFIER_LETTER_OGONEK, true) + .addCodePoint(MODIFIER_LETTER_OGONEK, true) + .addCodePoint('f', false) + .addCodePoint(MODIFIER_LETTER_OGONEK, true) + .addCodePoint(MODIFIER_LETTER_OGONEK, true) + .addKey(Constants.CODE_DELETE) + .addCodePoint('k', false); + for (final Event event : events) { + mLatinIME.onEvent(event); + } + assertEquals("double dead ogonek, and backspace", EXPECTED_RESULT, + mEditText.getText().toString()); + } + + public void testDeadCircumflexDeadDiaeresis() { + final int MODIFIER_LETTER_CIRCUMFLEX_ACCENT = 0x02C6; + final int MODIFIER_LETTER_DIAERESIS = 0xA8; + final String EXPECTED_RESULT = "r̂̈"; + + final EventList events = new EventList() + .addCodePoint(MODIFIER_LETTER_CIRCUMFLEX_ACCENT, true) + .addCodePoint(MODIFIER_LETTER_DIAERESIS, true) + .addCodePoint('r', false); + for (final Event event : events) { + mLatinIME.onEvent(event); + } + assertEquals("both circumflex and diaeresis on r", EXPECTED_RESULT, + mEditText.getText().toString()); + } + + public void testDeadCircumflexDeadDiaeresisBackspace() { + final int MODIFIER_LETTER_CIRCUMFLEX_ACCENT = 0x02C6; + final int MODIFIER_LETTER_DIAERESIS = 0xA8; + final String EXPECTED_RESULT = "û"; + + final EventList events = new EventList() + .addCodePoint(MODIFIER_LETTER_CIRCUMFLEX_ACCENT, true) + .addCodePoint(MODIFIER_LETTER_DIAERESIS, true) + .addKey(Constants.CODE_DELETE) + .addCodePoint('u', false); + for (final Event event : events) { + mLatinIME.onEvent(event); + } + assertEquals("dead circumflex, dead diaeresis, backspace, u", EXPECTED_RESULT, + mEditText.getText().toString()); + } + + public void testDeadCircumflexDoubleDeadDiaeresisBackspace() { + final int MODIFIER_LETTER_CIRCUMFLEX_ACCENT = 0x02C6; + final int MODIFIER_LETTER_DIAERESIS = 0xA8; + final String EXPECTED_RESULT = "\u02C6u"; + + final EventList events = new EventList() + .addCodePoint(MODIFIER_LETTER_CIRCUMFLEX_ACCENT, true) + .addCodePoint(MODIFIER_LETTER_DIAERESIS, true) + .addCodePoint(MODIFIER_LETTER_DIAERESIS, true) + .addKey(Constants.CODE_DELETE) + .addCodePoint('u', false); + for (final Event event : events) { + mLatinIME.onEvent(event); + } + assertEquals("dead circumflex, double dead diaeresis, backspace, u", EXPECTED_RESULT, + mEditText.getText().toString()); + } +} diff --git a/tests/src/com/android/inputmethod/latin/InputLogicTestsLanguageWithoutSpaces.java b/tests/src/com/android/inputmethod/latin/InputLogicTestsLanguageWithoutSpaces.java index 2560407dc..12461c96e 100644 --- a/tests/src/com/android/inputmethod/latin/InputLogicTestsLanguageWithoutSpaces.java +++ b/tests/src/com/android/inputmethod/latin/InputLogicTestsLanguageWithoutSpaces.java @@ -19,6 +19,8 @@ package com.android.inputmethod.latin; import android.test.suitebuilder.annotation.LargeTest; import android.view.inputmethod.BaseInputConnection; +import com.android.inputmethod.latin.common.Constants; + @LargeTest public class InputLogicTestsLanguageWithoutSpaces extends InputTestsBase { public void testAutoCorrectForLanguageWithoutSpaces() { @@ -74,7 +76,7 @@ public class InputLogicTestsLanguageWithoutSpaces extends InputTestsBase { mInputConnection.setSelection(CURSOR_POS, CURSOR_POS); mLatinIME.onUpdateSelection(typedLength, typedLength, CURSOR_POS, CURSOR_POS, -1, -1); - sleep(DELAY_TO_WAIT_FOR_PREDICTIONS); + sleep(DELAY_TO_WAIT_FOR_PREDICTIONS_MILLIS); runMessages(); assertEquals("start composing inside text", -1, BaseInputConnection.getComposingSpanStart(mEditText.getText())); @@ -87,11 +89,39 @@ public class InputLogicTestsLanguageWithoutSpaces extends InputTestsBase { BaseInputConnection.getComposingSpanEnd(mEditText.getText())); } + public void testMovingCursorInsideWordAndType() { + final String WORD_TO_TYPE = "abcdefgh"; + final int typedLength = WORD_TO_TYPE.length(); + final int CURSOR_POS = 4; + changeKeyboardLocaleAndDictLocale("th", "en_US"); + type(WORD_TO_TYPE); + mLatinIME.onUpdateSelection(0, 0, typedLength, typedLength, 0, typedLength); + sleep(DELAY_TO_WAIT_FOR_PREDICTIONS_MILLIS); + runMessages(); + mInputConnection.setSelection(CURSOR_POS, CURSOR_POS); + mLatinIME.onUpdateSelection(typedLength, typedLength, + CURSOR_POS, CURSOR_POS, 0, typedLength); + sleep(DELAY_TO_WAIT_FOR_PREDICTIONS_MILLIS); + runMessages(); + assertEquals("move cursor inside text", 0, + BaseInputConnection.getComposingSpanStart(mEditText.getText())); + assertEquals("move cursor inside text", typedLength, + BaseInputConnection.getComposingSpanEnd(mEditText.getText())); + type("x"); + sleep(DELAY_TO_WAIT_FOR_PREDICTIONS_MILLIS); + sleep(DELAY_TO_WAIT_FOR_PREDICTIONS_MILLIS); + runMessages(); + assertEquals("start typing while cursor inside composition", CURSOR_POS, + BaseInputConnection.getComposingSpanStart(mEditText.getText())); + assertEquals("start typing while cursor inside composition", CURSOR_POS + 1, + 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); + sleep(DELAY_TO_WAIT_FOR_PREDICTIONS_MILLIS); runMessages(); // Make sure there is no space assertEquals("predictions in lang without spaces", "Barack", diff --git a/tests/src/com/android/inputmethod/latin/InputLogicTestsNonEnglish.java b/tests/src/com/android/inputmethod/latin/InputLogicTestsNonEnglish.java deleted file mode 100644 index 866f8894c..000000000 --- a/tests/src/com/android/inputmethod/latin/InputLogicTestsNonEnglish.java +++ /dev/null @@ -1,124 +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.test.suitebuilder.annotation.LargeTest; - -@LargeTest -public class InputLogicTestsNonEnglish extends InputTestsBase { - final String NEXT_WORD_PREDICTION_OPTION = "next_word_prediction"; - - public void testAutoCorrectForFrench() { - final String STRING_TO_TYPE = "irq "; - final String EXPECTED_RESULT = "ira "; - changeLanguage("fr"); - type(STRING_TO_TYPE); - assertEquals("simple auto-correct for French", EXPECTED_RESULT, - mEditText.getText().toString()); - } - - public void testManualPickThenSeparatorForFrench() { - final String WORD1_TO_TYPE = "test"; - final String WORD2_TO_TYPE = "!"; - final String EXPECTED_RESULT = "test !"; - changeLanguage("fr"); - type(WORD1_TO_TYPE); - pickSuggestionManually(WORD1_TO_TYPE); - type(WORD2_TO_TYPE); - assertEquals("manual pick then separator for French", EXPECTED_RESULT, - mEditText.getText().toString()); - } - - public void testClusteringPunctuationForFrench() { - final String WORD1_TO_TYPE = "test"; - final String WORD2_TO_TYPE = "!!?!:!"; - // In English, the expected result would be "test!!?!:!" - final String EXPECTED_RESULT = "test !!?! : !"; - changeLanguage("fr"); - type(WORD1_TO_TYPE); - pickSuggestionManually(WORD1_TO_TYPE); - type(WORD2_TO_TYPE); - assertEquals("clustering punctuation for French", EXPECTED_RESULT, - mEditText.getText().toString()); - } - - public void testWordThenSpaceThenPunctuationFromStripTwiceForFrench() { - final String WORD_TO_TYPE = "test "; - final String PUNCTUATION_FROM_STRIP = "!"; - final String EXPECTED_RESULT = "test !!"; - final boolean defaultNextWordPredictionOption = - mLatinIME.getResources().getBoolean(R.bool.config_default_next_word_prediction); - final boolean previousNextWordPredictionOption = - setBooleanPreference(NEXT_WORD_PREDICTION_OPTION, false, - defaultNextWordPredictionOption); - try { - changeLanguage("fr"); - type(WORD_TO_TYPE); - sleep(DELAY_TO_WAIT_FOR_UNDERLINE); - runMessages(); - assertTrue("type word then type space should display punctuation strip", - mLatinIME.getSuggestedWordsForTest().isPunctuationSuggestions()); - pickSuggestionManually(PUNCTUATION_FROM_STRIP); - pickSuggestionManually(PUNCTUATION_FROM_STRIP); - assertEquals("type word then type space then punctuation from strip twice for French", - EXPECTED_RESULT, mEditText.getText().toString()); - } finally { - setBooleanPreference(NEXT_WORD_PREDICTION_OPTION, previousNextWordPredictionOption, - defaultNextWordPredictionOption); - } - } - - public void testWordThenSpaceDisplaysPredictions() { - final String WORD_TO_TYPE = "beaujolais "; - final String EXPECTED_RESULT = "nouveau"; - final boolean defaultNextWordPredictionOption = - mLatinIME.getResources().getBoolean(R.bool.config_default_next_word_prediction); - final boolean previousNextWordPredictionOption = - setBooleanPreference(NEXT_WORD_PREDICTION_OPTION, true, - defaultNextWordPredictionOption); - try { - changeLanguage("fr"); - type(WORD_TO_TYPE); - sleep(DELAY_TO_WAIT_FOR_UNDERLINE); - runMessages(); - final SuggestedWords suggestedWords = mLatinIME.getSuggestedWordsForTest(); - assertEquals("type word then type space yields predictions for French", - EXPECTED_RESULT, suggestedWords.size() > 0 ? suggestedWords.getWord(0) : null); - } finally { - setBooleanPreference(NEXT_WORD_PREDICTION_OPTION, previousNextWordPredictionOption, - defaultNextWordPredictionOption); - } - } - - public void testAutoCorrectForGerman() { - final String STRING_TO_TYPE = "unf "; - final String EXPECTED_RESULT = "und "; - changeLanguage("de"); - type(STRING_TO_TYPE); - assertEquals("simple auto-correct for German", EXPECTED_RESULT, - mEditText.getText().toString()); - } - - public void testAutoCorrectWithUmlautForGerman() { - final String STRING_TO_TYPE = "ueber "; - final String EXPECTED_RESULT = "über "; - changeLanguage("de"); - type(STRING_TO_TYPE); - assertEquals("auto-correct with umlaut for German", EXPECTED_RESULT, - mEditText.getText().toString()); - } -} diff --git a/tests/src/com/android/inputmethod/latin/InputLogicTestsReorderingMyanmar.java b/tests/src/com/android/inputmethod/latin/InputLogicTestsReorderingMyanmar.java deleted file mode 100644 index ab69c8592..000000000 --- a/tests/src/com/android/inputmethod/latin/InputLogicTestsReorderingMyanmar.java +++ /dev/null @@ -1,237 +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.test.suitebuilder.annotation.LargeTest; -import android.test.suitebuilder.annotation.Suppress; -import android.util.Pair; - -/* - * Relevant characters for this test : - * Spurs the need to reorder : - * U+1031 MYANMAR VOWEL SIGN E : ေ - * U+1004 U+103A U+1039 Kinzi. It's a compound character. - * - * List of consonants : - * U+1000 MYANMAR LETTER KA က - * U+1001 MYANMAR LETTER KHA ခ - * U+1002 MYANMAR LETTER GA ဂ - * U+1003 MYANMAR LETTER GHA ဃ - * U+1004 MYANMAR LETTER NGA င - * U+1005 MYANMAR LETTER CA စ - * U+1006 MYANMAR LETTER CHA ဆ - * U+1007 MYANMAR LETTER JA ဇ - * U+1008 MYANMAR LETTER JHA ဈ - * U+1009 MYANMAR LETTER NYA ဉ - * U+100A MYANMAR LETTER NNYA ည - * U+100B MYANMAR LETTER TTA ဋ - * U+100C MYANMAR LETTER TTHA ဌ - * U+100D MYANMAR LETTER DDA ဍ - * U+100E MYANMAR LETTER DDHA ဎ - * U+100F MYANMAR LETTER NNA ဏ - * U+1010 MYANMAR LETTER TA တ - * U+1011 MYANMAR LETTER THA ထ - * U+1012 MYANMAR LETTER DA ဒ - * U+1013 MYANMAR LETTER DHA ဓ - * U+1014 MYANMAR LETTER NA န - * U+1015 MYANMAR LETTER PA ပ - * U+1016 MYANMAR LETTER PHA ဖ - * U+1017 MYANMAR LETTER BA ဗ - * U+1018 MYANMAR LETTER BHA ဘ - * U+1019 MYANMAR LETTER MA မ - * U+101A MYANMAR LETTER YA ယ - * U+101B MYANMAR LETTER RA ရ - * U+101C MYANMAR LETTER LA လ - * U+101D MYANMAR LETTER WA ဝ - * U+101E MYANMAR LETTER SA သ - * U+101F MYANMAR LETTER HA ဟ - * U+1020 MYANMAR LETTER LLA ဠ - * U+103F MYANMAR LETTER GREAT SA ဿ - * - * List of medials : - * U+103B MYANMAR CONSONANT SIGN MEDIAL YA ျ - * U+103C MYANMAR CONSONANT SIGN MEDIAL RA ြ - * U+103D MYANMAR CONSONANT SIGN MEDIAL WA ွ - * U+103E MYANMAR CONSONANT SIGN MEDIAL HA ှ - * U+105E MYANMAR CONSONANT SIGN MON MEDIAL NA ၞ - * U+105F MYANMAR CONSONANT SIGN MON MEDIAL MA ၟ - * U+1060 MYANMAR CONSONANT SIGN MON MEDIAL LA ၠ - * U+1082 MYANMAR CONSONANT SIGN SHAN MEDIAL WA ႂ - * - * Other relevant characters : - * U+200C ZERO WIDTH NON-JOINER - * U+200B ZERO WIDTH SPACE - */ - -@LargeTest -// These tests are inactive until the combining code for Myanmar Reordering is sorted out. -@Suppress -@SuppressWarnings("rawtypes") -public class InputLogicTestsReorderingMyanmar extends InputTestsBase { - // The tests are formatted as follows. - // Each test is an entry in the array of Pair arrays. - - // One test is an array of pairs. Each pair contains, in the `first' member, - // the code points that the next key press should contain. In the `second' - // member is stored the string that should be in the text view after this - // key press. - - private static final Pair[][] TESTS = { - - // Tests for U+1031 MYANMAR VOWEL SIGN E : ေ - new Pair[] { // Type : U+1031 U+1000 U+101F ေ က ဟ - Pair.create(new int[] { 0x1031 }, "\u1031"), // ေ - Pair.create(new int[] { 0x1000 }, "\u1000\u1031"), // ကေ - Pair.create(new int[] { 0x101F }, "\u1000\u1031\u101F") // ကေဟ - }, - - new Pair[] { // Type : U+1000 U+1031 U+101F က ေ ဟ - Pair.create(new int[] { 0x1000 }, "\u1000"), // က - Pair.create(new int[] { 0x1031 }, "\u1000\u200B\u1031"), // ကေ - Pair.create(new int[] { 0x101F }, "\u1000\u101F\u1031") // ကဟေ - }, - - new Pair[] { // Type : U+1031 U+101D U+103E U+1018 ေ ဝ ှ ဘ - Pair.create(new int[] { 0x1031 }, "\u1031"), // ေ - Pair.create(new int[] { 0x101D }, "\u101D\u1031"), // ဝေ - Pair.create(new int[] { 0x103E }, "\u101D\u103E\u1031"), // ဝှေ - Pair.create(new int[] { 0x1018 }, "\u101D\u103E\u1031\u1018") // ဝှေဘ - }, - - new Pair[] { // Type : U+1031 U+1014 U+1031 U+1000 U+102C U+1004 U+103A U+1038 U+101C - // U+102C U+1038 U+104B ေ န ေ က ာ င ် း လ ာ း ။ - Pair.create(new int[] { 0x1031 }, "\u1031"), // ေ - Pair.create(new int[] { 0x1014 }, "\u1014\u1031"), // နေ - Pair.create(new int[] { 0x1031 }, "\u1014\u1031\u1031"), // နေေ - Pair.create(new int[] { 0x1000 }, "\u1014\u1031\u1000\u1031"), // နေကေ - Pair.create(new int[] { 0x102C }, "\u1014\u1031\u1000\u1031\u102C"), // နေကော - Pair.create(new int[] { 0x1004 }, "\u1014\u1031\u1000\u1031\u102C\u1004"), // နေကောင - Pair.create(new int[] { 0x103A }, // နေကောင် - "\u1014\u1031\u1000\u1031\u102C\u1004\u103A"), - Pair.create(new int[] { 0x1038 }, // နေကောင်း - "\u1014\u1031\u1000\u1031\u102C\u1004\u103A\u1038"), - Pair.create(new int[] { 0x101C }, // နေကောင်းလ - "\u1014\u1031\u1000\u1031\u102C\u1004\u103A\u1038\u101C"), - Pair.create(new int[] { 0x102C }, // နေကောင်းလာ - "\u1014\u1031\u1000\u1031\u102C\u1004\u103A\u1038\u101C\u102C"), - Pair.create(new int[] { 0x1038 }, // နေကောင်းလား - "\u1014\u1031\u1000\u1031\u102C\u1004\u103A\u1038\u101C\u102C\u1038"), - Pair.create(new int[] { 0x104B }, // နေကောင်းလား။ - "\u1014\u1031\u1000\u1031\u102C\u1004\u103A\u1038\u101C\u102C\u1038\u104B") - }, - - new Pair[] { // Type : U+1031 U+1031 U+1031 U+1000 ေ ေ ေ က - Pair.create(new int[] { 0x1031 }, "\u1031"), // ေ - Pair.create(new int[] { 0x1031 }, "\u1031\u1031"), // ေေ - Pair.create(new int[] { 0x1031 }, "\u1031\u1031\u1031"), // U+1031ေေေ - Pair.create(new int[] { 0x1000 }, "\u1031\u1031\u1000\u1031") // ေေကေ - }, - - new Pair[] { // Type : U+1031 U+1001 U+103B U+103D U+1038 ေ ခ ျ ွ း - Pair.create(new int[] { 0x1031 }, "\u1031"), // ေ - Pair.create(new int[] { 0x1001 }, "\u1001\u1031"), // ခေ - Pair.create(new int[] { 0x103B }, "\u1001\u103B\u1031"), // ချေ - Pair.create(new int[] { 0x103D }, "\u1001\u103B\u103D\u1031"), // ချွေ - Pair.create(new int[] { 0x1038 }, "\u1001\u103B\u103D\u1031\u1038") // ချွေး - }, - - // Tests for Kinzi U+1004 U+103A U+1039 : - - /* Kinzi reordering is not implemented yet. Uncomment these tests when it is. - - new Pair[] { // Type : U+1021 U+1002 (U+1004 U+103A U+1039) - // U+101C U+1014 U+103A အ ဂ (င ် ္) လ န ် - Pair.create(new int[] { 0x1021 }, "\u1021"), // အ - Pair.create(new int[] { 0x1002 }, "\u1021\u1002"), // အဂ - Pair.create(new int[] { 0x1004, 0x103A, 0x1039 }, // အင်္ဂ - "\u1021\u1004\u103A\u1039\u1002"), - Pair.create(new int[] { 0x101C }, // အင်္ဂလ - "\u1021\u1004\u103A\u1039\u1002\u101C"), - Pair.create(new int[] { 0x1014 }, // အင်္ဂလန - "\u1021\u1004\u103A\u1039\u1002\u101C\u1014"), - Pair.create(new int[] { 0x103A }, // အင်္ဂလန် - "\u1021\u1004\u103A\u1039\u1002\u101C\u1014\u103A") - }, - - new Pair[] { //Type : kinzi after a whole syllable U+101E U+1001 U+103B U+102D U+102F - // (U+1004 U+103A U+1039) U+1004 U+103A U+1038 သ ခ ျ ိ ု င ် ္ င ် း - Pair.create(new int[] { 0x101E }, "\u101E"), // သခ - Pair.create(new int[] { 0x1001 }, "\u101E\u1001"), // သခ - Pair.create(new int[] { 0x103B }, "\u101E\u1001\u103B"), // သချ - Pair.create(new int[] { 0x102D }, "\u101E\u1001\u103B\u102D"), // သချိ - Pair.create(new int[] { 0x102F }, "\u101E\u1001\u103B\u102D\u102F"), // သချို - Pair.create(new int[] { 0x1004, 0x103A, 0x1039}, // သင်္ချို - "\u101E\u1004\u103A\u1039\u1001\u103B\u102D\u102F"), - Pair.create(new int[] { 0x1004 }, // သင်္ချိုင - "\u101E\u1004\u103A\u1039\u1001\u103B\u102D\u102F\u1004"), - Pair.create(new int[] { 0x103A }, // သင်္ချိုင် - "\u101E\u1004\u103A\u1039\u1001\u103B\u102D\u102F\u1004\u103A"), - Pair.create(new int[] { 0x1038 }, // သင်္ချိုင်း - "\u101E\u1004\u103A\u1039\u1001\u103B\u102D\u102F\u1004\u103A\u1038") - }, - - new Pair[] { // Type : kinzi after the consonant U+101E U+1001 (U+1004 U+103A U+1039) - // U+103B U+102D U+102F U+1004 U+103A U+1038 သ ခ င ် ္ ျ ိ ု င ် း - Pair.create(new int[] { 0x101E }, "\u101E"), // သခ - Pair.create(new int[] { 0x1001 }, "\u101E\u1001"), // သခ - Pair.create(new int[] { 0x1004, 0x103A, 0x1039 }, // သင်္ခ - "\u101E\u1004\u103A\u1039\u1001"), - Pair.create(new int[] { 0x103B }, // သင်္ချ - "\u101E\u1004\u103A\u1039\u1001\u103B"), - Pair.create(new int[] { 0x102D }, // သင်္ချိ - "\u101E\u1004\u103A\u1039\u1001\u103B\u102D"), - Pair.create(new int[] { 0x102F }, // သင်္ချို - "\u101E\u1004\u103A\u1039\u1001\u103B\u102D\u102F"), - Pair.create(new int[] { 0x1004 }, // သင်္ချိုင - "\u101E\u1004\u103A\u1039\u1001\u103B\u102D\u102F\u1004"), - Pair.create(new int[] { 0x103A }, // သင်္ချိုင် - "\u101E\u1004\u103A\u1039\u1001\u103B\u102D\u102F\u1004\u103A"), - Pair.create(new int[] { 0x1038 }, // သင်္ချိုင်း - "\u101E\u1004\u103A\u1039\u1001\u103B\u102D\u102F\u1004\u103A\u1038") - }, - */ - }; - - @SuppressWarnings("unchecked") - private void doMyanmarTest(final int testNumber, final Pair[] test) { - int stepNumber = 0; - for (final Pair<int[], String> step : test) { - ++stepNumber; - final int[] input = step.first; - final String expectedResult = step.second; - if (input.length > 1) { - mLatinIME.onTextInput(new String(input, 0, input.length)); - } else { - type(input[0]); - } - assertEquals("Myanmar reordering test " + testNumber + ", step " + stepNumber, - expectedResult, mEditText.getText().toString()); - } - } - - public void testMyanmarReordering() { - int testNumber = 0; - changeLanguage("my_MM", "CombiningRules=MyanmarReordering"); - for (final Pair[] test : TESTS) { - // Small trick to reset LatinIME : setText("") and send updateSelection with values - // LatinIME has never seen, and cursor pos 0,0. - mEditText.setText(""); - mLatinIME.onUpdateSelection(1, 1, 0, 0, -1, -1); - doMyanmarTest(++testNumber, test); - } - } -} diff --git a/tests/src/com/android/inputmethod/latin/InputTestsBase.java b/tests/src/com/android/inputmethod/latin/InputTestsBase.java index 986fb1097..dead53da2 100644 --- a/tests/src/com/android/inputmethod/latin/InputTestsBase.java +++ b/tests/src/com/android/inputmethod/latin/InputTestsBase.java @@ -18,6 +18,7 @@ package com.android.inputmethod.latin; import android.content.Context; import android.content.SharedPreferences; +import android.graphics.Point; import android.os.Looper; import android.preference.PreferenceManager; import android.test.ServiceTestCase; @@ -36,12 +37,17 @@ import android.widget.EditText; import android.widget.FrameLayout; import com.android.inputmethod.compat.InputMethodSubtypeCompatUtils; +import com.android.inputmethod.event.Event; import com.android.inputmethod.keyboard.Key; import com.android.inputmethod.keyboard.Keyboard; +import com.android.inputmethod.latin.Dictionary.PhonyDictionary; import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; +import com.android.inputmethod.latin.common.Constants; +import com.android.inputmethod.latin.common.InputPointers; +import com.android.inputmethod.latin.common.LocaleUtils; +import com.android.inputmethod.latin.common.StringUtils; import com.android.inputmethod.latin.settings.DebugSettings; import com.android.inputmethod.latin.settings.Settings; -import com.android.inputmethod.latin.utils.LocaleUtils; import com.android.inputmethod.latin.utils.SubtypeLocaleUtils; import java.util.Locale; @@ -52,21 +58,28 @@ public class InputTestsBase extends ServiceTestCase<LatinIMEForTests> { // Default value for auto-correction threshold. This is the string representation of the // index in the resources array of auto-correction threshold settings. - private static final String DEFAULT_AUTO_CORRECTION_THRESHOLD = "1"; + private static final boolean DEFAULT_AUTO_CORRECTION = true; // The message that sets the underline is posted with a 500 ms delay - protected static final int DELAY_TO_WAIT_FOR_UNDERLINE = 500; + protected static final int DELAY_TO_WAIT_FOR_UNDERLINE_MILLIS = 500; // The message that sets predictions is posted with a 200 ms delay - protected static final int DELAY_TO_WAIT_FOR_PREDICTIONS = 200; - private final int TIMEOUT_TO_WAIT_FOR_LOADING_MAIN_DICTIONARY_IN_SECONDS = 60; + protected static final int DELAY_TO_WAIT_FOR_PREDICTIONS_MILLIS = 200; + // We wait for gesture computation for this delay + protected static final int DELAY_TO_WAIT_FOR_GESTURE_MILLIS = 200; + // If a dictionary takes longer to load, we could have serious problems. + private final int TIMEOUT_TO_WAIT_FOR_LOADING_MAIN_DICTIONARY_IN_SECONDS = 5; + + // Type for a test phony dictionary + private static final String TYPE_TEST = "test"; + private static final PhonyDictionary DICTIONARY_TEST = new PhonyDictionary(TYPE_TEST); protected LatinIME mLatinIME; protected Keyboard mKeyboard; protected MyEditText mEditText; protected View mInputView; protected InputConnection mInputConnection; + private boolean mPreviousAutoCorrectSetting; private boolean mPreviousBigramPredictionSettings; - private String mPreviousAutoCorrectSetting; // A helper class to ease span tests public static class SpanGetter { @@ -94,12 +107,15 @@ public class InputTestsBase extends ServiceTestCase<LatinIMEForTests> { throw new RuntimeException("Expected one span, found " + spans.length); } } + public SuggestionSpan getSpan() { + return (SuggestionSpan) mSpan; + } public boolean isAutoCorrectionIndicator() { return (mSpan instanceof SuggestionSpan) && - 0 != (SuggestionSpan.FLAG_AUTO_CORRECTION & ((SuggestionSpan)mSpan).getFlags()); + 0 != (SuggestionSpan.FLAG_AUTO_CORRECTION & getSpan().getFlags()); } public String[] getSuggestions() { - return ((SuggestionSpan)mSpan).getSuggestions(); + return getSpan().getSuggestions(); } } @@ -140,8 +156,6 @@ public class InputTestsBase extends ServiceTestCase<LatinIMEForTests> { super(LatinIMEForTests.class); } - // TODO: Isn't there a way to make this generic somehow? We can take a <T> and return a <T> - // but we'd have to dispatch types on editor.put...() functions protected boolean setBooleanPreference(final String key, final boolean value, final boolean defaultValue) { final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(mLatinIME); @@ -152,6 +166,11 @@ public class InputTestsBase extends ServiceTestCase<LatinIMEForTests> { return previousSetting; } + protected boolean getBooleanPreference(final String key, final boolean defaultValue) { + return PreferenceManager.getDefaultSharedPreferences(mLatinIME) + .getBoolean(key, defaultValue); + } + protected String setStringPreference(final String key, final String value, final String defaultValue) { final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(mLatinIME); @@ -182,13 +201,17 @@ public class InputTestsBase extends ServiceTestCase<LatinIMEForTests> { | InputType.TYPE_TEXT_FLAG_MULTI_LINE; mEditText.setInputType(inputType); mEditText.setEnabled(true); + mLastCursorPos = 0; + if (null == Looper.myLooper()) { + Looper.prepare(); + } setupService(); mLatinIME = getService(); setDebugMode(true); mPreviousBigramPredictionSettings = setBooleanPreference(Settings.PREF_BIGRAM_PREDICTIONS, true, true /* defaultValue */); - mPreviousAutoCorrectSetting = setStringPreference(Settings.PREF_AUTO_CORRECTION_THRESHOLD, - DEFAULT_AUTO_CORRECTION_THRESHOLD, DEFAULT_AUTO_CORRECTION_THRESHOLD); + mPreviousAutoCorrectSetting = setBooleanPreference(Settings.PREF_AUTO_CORRECTION, + DEFAULT_AUTO_CORRECTION, DEFAULT_AUTO_CORRECTION); mLatinIME.onCreate(); EditorInfo ei = new EditorInfo(); final InputConnection ic = mEditText.onCreateInputConnection(ei); @@ -207,7 +230,7 @@ public class InputTestsBase extends ServiceTestCase<LatinIMEForTests> { // Run messages to avoid the messages enqueued by startInputView() and its friends // to run on a later call and ruin things. We need to wait first because some of them // can be posted with a delay (notably, MSG_RESUME_SUGGESTIONS) - sleep(DELAY_TO_WAIT_FOR_PREDICTIONS); + sleep(DELAY_TO_WAIT_FOR_PREDICTIONS_MILLIS); runMessages(); } @@ -219,8 +242,8 @@ public class InputTestsBase extends ServiceTestCase<LatinIMEForTests> { mLatinIME.mHandler.removeAllMessages(); setBooleanPreference(Settings.PREF_BIGRAM_PREDICTIONS, mPreviousBigramPredictionSettings, true /* defaultValue */); - setStringPreference(Settings.PREF_AUTO_CORRECTION_THRESHOLD, mPreviousAutoCorrectSetting, - DEFAULT_AUTO_CORRECTION_THRESHOLD); + setBooleanPreference(Settings.PREF_AUTO_CORRECTION, mPreviousAutoCorrectSetting, + DEFAULT_AUTO_CORRECTION); setDebugMode(false); mLatinIME.recycle(); super.tearDown(); @@ -236,7 +259,7 @@ public class InputTestsBase extends ServiceTestCase<LatinIMEForTests> { // Now, Looper#loop() never exits in normal operation unless the Looper#quit() method // is called, which has a lot of bad side effects. We can however just throw an exception // in the runnable which will unwind the stack and allow us to exit. - private final class InterruptRunMessagesException extends RuntimeException { + final class InterruptRunMessagesException extends RuntimeException { // Empty class } protected void runMessages() { @@ -263,14 +286,16 @@ public class InputTestsBase extends ServiceTestCase<LatinIMEForTests> { // but keep them in mind if something breaks. Commenting them out as is should work. //mLatinIME.onPressKey(codePoint, 0 /* repeatCount */, true /* isSinglePointer */); final Key key = mKeyboard.getKey(codePoint); + final Event event; if (key == null) { - mLatinIME.onCodeInput(codePoint, Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE, - isKeyRepeat); + event = Event.createSoftwareKeypressEvent(codePoint, Event.NOT_A_KEY_CODE, + Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE, isKeyRepeat); } else { final int x = key.getX() + key.getWidth() / 2; final int y = key.getY() + key.getHeight() / 2; - mLatinIME.onCodeInput(codePoint, x, y, isKeyRepeat); + event = LatinIME.createSoftwareKeypressEvent(codePoint, x, y, isKeyRepeat); } + mLatinIME.onEvent(event); // Also see the comment at the top of this function about onReleaseKey //mLatinIME.onReleaseKey(codePoint, false /* withSliding */); } @@ -289,6 +314,46 @@ public class InputTestsBase extends ServiceTestCase<LatinIMEForTests> { } } + protected Point getXY(final int codePoint) { + final Key key = mKeyboard.getKey(codePoint); + if (key == null) { + throw new RuntimeException("Code point not on the keyboard"); + } + return new Point(key.getX() + key.getWidth() / 2, key.getY() + key.getHeight() / 2); + } + + protected void gesture(final String stringToGesture) { + if (StringUtils.codePointCount(stringToGesture) < 2) { + throw new RuntimeException("Can't gesture strings less than 2 chars long"); + } + + mLatinIME.onStartBatchInput(); + final int startCodePoint = stringToGesture.codePointAt(0); + Point oldPoint = getXY(startCodePoint); + int timestamp = 0; // In milliseconds since the start of the gesture + final InputPointers pointers = new InputPointers(Constants.DEFAULT_GESTURE_POINTS_CAPACITY); + pointers.addPointer(oldPoint.x, oldPoint.y, 0 /* pointerId */, timestamp); + + for (int i = Character.charCount(startCodePoint); i < stringToGesture.length(); + i = stringToGesture.offsetByCodePoints(i, 1)) { + final Point newPoint = getXY(stringToGesture.codePointAt(i)); + // Arbitrarily 0.5s between letters and 0.1 between events. Refine this later if needed. + final int STEPS = 5; + for (int j = 0; j < STEPS; ++j) { + timestamp += 100; + pointers.addPointer(oldPoint.x + ((newPoint.x - oldPoint.x) * j) / STEPS, + oldPoint.y + ((newPoint.y - oldPoint.y) * j) / STEPS, + 0 /* pointerId */, timestamp); + } + oldPoint.x = newPoint.x; + oldPoint.y = newPoint.y; + mLatinIME.onUpdateBatchInput(pointers); + } + mLatinIME.onEndBatchInput(pointers); + sleep(DELAY_TO_WAIT_FOR_GESTURE_MILLIS); + runMessages(); + } + protected void waitForDictionariesToBeLoaded() { try { mLatinIME.waitForLoadingDictionaries( @@ -329,7 +394,7 @@ public class InputTestsBase extends ServiceTestCase<LatinIMEForTests> { false /* isAuxiliary */, false /* overridesImplicitlyEnabledSubtype */, 0 /* id */); - SubtypeSwitcher.getInstance().forceSubtype(subtype); + RichInputMethodManager.forceSubtype(subtype); mLatinIME.onCurrentInputMethodSubtypeChanged(subtype); runMessages(); mKeyboard = mLatinIME.mKeyboardSwitcher.getKeyboard(); @@ -346,8 +411,9 @@ public class InputTestsBase extends ServiceTestCase<LatinIMEForTests> { } protected void pickSuggestionManually(final String suggestion) { - mLatinIME.pickSuggestionManually(new SuggestedWordInfo(suggestion, 1, - SuggestedWordInfo.KIND_CORRECTION, null /* sourceDict */, + mLatinIME.pickSuggestionManually(new SuggestedWordInfo(suggestion, + "" /* prevWordsContext */, 1 /* score */, + SuggestedWordInfo.KIND_CORRECTION, DICTIONARY_TEST, SuggestedWordInfo.NOT_AN_INDEX /* indexOfTouchPointOfSecondWord */, SuggestedWordInfo.NOT_A_CONFIDENCE /* autoCommitFirstWordConfidence */)); } @@ -358,4 +424,40 @@ public class InputTestsBase extends ServiceTestCase<LatinIMEForTests> { Thread.sleep(milliseconds); } catch (InterruptedException e) {} } + + // Some helper methods to manage the mock cursor position + // DO NOT CALL LatinIME#onUpdateSelection IF YOU WANT TO USE THOSE + int mLastCursorPos = 0; + /** + * Move the cached cursor position to the passed position and send onUpdateSelection to LatinIME + */ + protected int sendUpdateForCursorMoveTo(final int position) { + mInputConnection.setSelection(position, position); + mLatinIME.onUpdateSelection(mLastCursorPos, mLastCursorPos, position, position, -1, -1); + mLastCursorPos = position; + return position; + } + + /** + * Move the cached cursor position by the passed amount and send onUpdateSelection to LatinIME + */ + protected int sendUpdateForCursorMoveBy(final int offset) { + final int lastPos = mEditText.getText().length(); + final int requestedPosition = mLastCursorPos + offset; + if (requestedPosition < 0) { + return sendUpdateForCursorMoveTo(0); + } else if (requestedPosition > lastPos) { + return sendUpdateForCursorMoveTo(lastPos); + } else { + return sendUpdateForCursorMoveTo(requestedPosition); + } + } + + /** + * Move the cached cursor position to the end of the line and send onUpdateSelection to LatinIME + */ + protected int sendUpdateForCursorMoveToEndOfLine() { + final int lastPos = mEditText.getText().length(); + return sendUpdateForCursorMoveTo(lastPos); + } } diff --git a/tests/src/com/android/inputmethod/latin/LatinIMEForTests.java b/tests/src/com/android/inputmethod/latin/LatinIMEForTests.java index e47c55736..035c8d7ce 100644 --- a/tests/src/com/android/inputmethod/latin/LatinIMEForTests.java +++ b/tests/src/com/android/inputmethod/latin/LatinIMEForTests.java @@ -21,4 +21,16 @@ public class LatinIMEForTests extends LatinIME { public boolean isInputViewShown() { return true; } + + private boolean deallocateMemoryWasPerformed = false; + + @Override + protected void deallocateMemory() { + super.deallocateMemory(); + deallocateMemoryWasPerformed = true; + } + + public boolean getDeallocateMemoryWasPerformed() { + return deallocateMemoryWasPerformed; + } } diff --git a/tests/src/com/android/inputmethod/latin/LatinImeStressTests.java b/tests/src/com/android/inputmethod/latin/LatinImeStressTests.java index f5e993de8..22114b7a0 100644 --- a/tests/src/com/android/inputmethod/latin/LatinImeStressTests.java +++ b/tests/src/com/android/inputmethod/latin/LatinImeStressTests.java @@ -18,7 +18,7 @@ package com.android.inputmethod.latin; import android.test.suitebuilder.annotation.LargeTest; -import com.android.inputmethod.latin.makedict.CodePointUtils; +import com.android.inputmethod.latin.common.CodePointUtils; import java.util.Random; diff --git a/tests/src/com/android/inputmethod/latin/LatinImeTests.java b/tests/src/com/android/inputmethod/latin/LatinImeTests.java new file mode 100644 index 000000000..c6f631328 --- /dev/null +++ b/tests/src/com/android/inputmethod/latin/LatinImeTests.java @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.inputmethod.latin; + +import android.test.suitebuilder.annotation.LargeTest; + +@LargeTest +public class LatinImeTests extends InputTestsBase { + public void testDeferredDeallocation_doesntHappenBeforeTimeout() { + mLatinIME.mHandler.onFinishInputView(true); + runMessages(); + sleep(1000); // 1s + runMessages(); + assertFalse("memory deallocation performed before timeout passed", + ((LatinIMEForTests)mLatinIME).getDeallocateMemoryWasPerformed()); + } + + public void testDeferredDeallocation_doesHappenAfterTimeout() { + mLatinIME.mHandler.onFinishInputView(true); + runMessages(); + sleep(11000); // 11s (timeout is at 10s) + runMessages(); + assertTrue("memory deallocation not performed although timeout passed", + ((LatinIMEForTests)mLatinIME).getDeallocateMemoryWasPerformed()); + } +} diff --git a/tests/src/com/android/inputmethod/latin/NgramContextTests.java b/tests/src/com/android/inputmethod/latin/NgramContextTests.java new file mode 100644 index 000000000..0a662db82 --- /dev/null +++ b/tests/src/com/android/inputmethod/latin/NgramContextTests.java @@ -0,0 +1,145 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.inputmethod.latin; + +import com.android.inputmethod.latin.NgramContext.WordInfo; +import com.android.inputmethod.latin.settings.SpacingAndPunctuations; +import com.android.inputmethod.latin.utils.NgramContextUtils; + +import android.test.AndroidTestCase; +import android.test.suitebuilder.annotation.SmallTest; + +@SmallTest +public class NgramContextTests extends AndroidTestCase { + public void testConstruct() { + assertEquals(new NgramContext(new WordInfo("a")), new NgramContext(new WordInfo("a"))); + assertEquals(new NgramContext(WordInfo.BEGINNING_OF_SENTENCE_WORD_INFO), + new NgramContext(WordInfo.BEGINNING_OF_SENTENCE_WORD_INFO)); + assertEquals(new NgramContext(WordInfo.EMPTY_WORD_INFO), + new NgramContext(WordInfo.EMPTY_WORD_INFO)); + assertEquals(new NgramContext(WordInfo.EMPTY_WORD_INFO), + new NgramContext(WordInfo.EMPTY_WORD_INFO)); + } + + public void testIsBeginningOfSentenceContext() { + assertFalse(new NgramContext().isBeginningOfSentenceContext()); + assertTrue(new NgramContext(WordInfo.BEGINNING_OF_SENTENCE_WORD_INFO) + .isBeginningOfSentenceContext()); + assertTrue(NgramContext.BEGINNING_OF_SENTENCE.isBeginningOfSentenceContext()); + assertFalse(new NgramContext(new WordInfo("a")).isBeginningOfSentenceContext()); + assertFalse(new NgramContext(new WordInfo("")).isBeginningOfSentenceContext()); + assertFalse(new NgramContext(WordInfo.EMPTY_WORD_INFO).isBeginningOfSentenceContext()); + assertTrue(new NgramContext(WordInfo.BEGINNING_OF_SENTENCE_WORD_INFO, new WordInfo("a")) + .isBeginningOfSentenceContext()); + assertFalse(new NgramContext(new WordInfo("a"), WordInfo.BEGINNING_OF_SENTENCE_WORD_INFO) + .isBeginningOfSentenceContext()); + assertFalse(new NgramContext( + WordInfo.EMPTY_WORD_INFO, WordInfo.BEGINNING_OF_SENTENCE_WORD_INFO) + .isBeginningOfSentenceContext()); + } + + public void testGetNextNgramContext() { + final NgramContext ngramContext_a = new NgramContext(new WordInfo("a")); + final NgramContext ngramContext_b_a = + ngramContext_a.getNextNgramContext(new WordInfo("b")); + assertEquals("b", ngramContext_b_a.getNthPrevWord(1)); + assertEquals("a", ngramContext_b_a.getNthPrevWord(2)); + final NgramContext ngramContext_bos_b = + ngramContext_b_a.getNextNgramContext(WordInfo.BEGINNING_OF_SENTENCE_WORD_INFO); + assertTrue(ngramContext_bos_b.isBeginningOfSentenceContext()); + assertEquals("b", ngramContext_bos_b.getNthPrevWord(2)); + final NgramContext ngramContext_c_bos = + ngramContext_b_a.getNextNgramContext(new WordInfo("c")); + assertEquals("c", ngramContext_c_bos.getNthPrevWord(1)); + } + + public void testExtractPrevWordsContextTest() { + final NgramContext ngramContext_bos = + new NgramContext(WordInfo.BEGINNING_OF_SENTENCE_WORD_INFO); + assertEquals("<S>", ngramContext_bos.extractPrevWordsContext()); + final NgramContext ngramContext_a = new NgramContext(new WordInfo("a")); + final NgramContext ngramContext_b_a = + ngramContext_a.getNextNgramContext(new WordInfo("b")); + assertEquals("b", ngramContext_b_a.getNthPrevWord(1)); + assertEquals("a", ngramContext_b_a.getNthPrevWord(2)); + assertEquals("a b", ngramContext_b_a.extractPrevWordsContext()); + + final NgramContext ngramContext_bos_b = + ngramContext_b_a.getNextNgramContext(WordInfo.BEGINNING_OF_SENTENCE_WORD_INFO); + assertTrue(ngramContext_bos_b.isBeginningOfSentenceContext()); + assertEquals("b", ngramContext_bos_b.getNthPrevWord(2)); + assertEquals("a b <S>", ngramContext_bos_b.extractPrevWordsContext()); + + final NgramContext ngramContext_empty = new NgramContext(WordInfo.EMPTY_WORD_INFO); + assertEquals("", ngramContext_empty.extractPrevWordsContext()); + final NgramContext ngramContext_a_empty = + ngramContext_empty.getNextNgramContext(new WordInfo("a")); + assertEquals("a", ngramContext_a_empty.getNthPrevWord(1)); + assertEquals("a", ngramContext_a_empty.extractPrevWordsContext()); + } + + public void testExtractPrevWordsContextArray() { + final NgramContext ngramContext_bos = + new NgramContext(WordInfo.BEGINNING_OF_SENTENCE_WORD_INFO); + assertEquals("<S>", ngramContext_bos.extractPrevWordsContext()); + assertEquals(1, ngramContext_bos.extractPrevWordsContextArray().length); + final NgramContext ngramContext_a = new NgramContext(new WordInfo("a")); + final NgramContext ngramContext_b_a = + ngramContext_a.getNextNgramContext(new WordInfo("b")); + assertEquals(2, ngramContext_b_a.extractPrevWordsContextArray().length); + assertEquals("b", ngramContext_b_a.getNthPrevWord(1)); + assertEquals("a", ngramContext_b_a.getNthPrevWord(2)); + assertEquals("a", ngramContext_b_a.extractPrevWordsContextArray()[0]); + assertEquals("b", ngramContext_b_a.extractPrevWordsContextArray()[1]); + + final NgramContext ngramContext_bos_b = + ngramContext_b_a.getNextNgramContext(WordInfo.BEGINNING_OF_SENTENCE_WORD_INFO); + assertTrue(ngramContext_bos_b.isBeginningOfSentenceContext()); + assertEquals(3, ngramContext_bos_b.extractPrevWordsContextArray().length); + assertEquals("b", ngramContext_bos_b.getNthPrevWord(2)); + assertEquals("a", ngramContext_bos_b.extractPrevWordsContextArray()[0]); + assertEquals("b", ngramContext_bos_b.extractPrevWordsContextArray()[1]); + assertEquals("<S>", ngramContext_bos_b.extractPrevWordsContextArray()[2]); + + final NgramContext ngramContext_empty = new NgramContext(WordInfo.EMPTY_WORD_INFO); + assertEquals(0, ngramContext_empty.extractPrevWordsContextArray().length); + final NgramContext ngramContext_a_empty = + ngramContext_empty.getNextNgramContext(new WordInfo("a")); + assertEquals(1, ngramContext_a_empty.extractPrevWordsContextArray().length); + assertEquals("a", ngramContext_a_empty.extractPrevWordsContextArray()[0]); + } + + public void testGetNgramContextFromNthPreviousWord() { + SpacingAndPunctuations spacingAndPunctuations = new SpacingAndPunctuations( + mContext.getResources()); + assertEquals("<S>", NgramContextUtils.getNgramContextFromNthPreviousWord("", + spacingAndPunctuations, 1).extractPrevWordsContext()); + assertEquals("<S> b", NgramContextUtils.getNgramContextFromNthPreviousWord("a. b ", + spacingAndPunctuations, 1).extractPrevWordsContext()); + assertEquals("<S> b", NgramContextUtils.getNgramContextFromNthPreviousWord("a? b ", + spacingAndPunctuations, 1).extractPrevWordsContext()); + assertEquals("<S> b", NgramContextUtils.getNgramContextFromNthPreviousWord("a! b ", + spacingAndPunctuations, 1).extractPrevWordsContext()); + assertEquals("<S> b", NgramContextUtils.getNgramContextFromNthPreviousWord("a\nb ", + spacingAndPunctuations, 1).extractPrevWordsContext()); + assertEquals("<S> a b", NgramContextUtils.getNgramContextFromNthPreviousWord("a b ", + spacingAndPunctuations, 1).extractPrevWordsContext()); + assertFalse(NgramContextUtils + .getNgramContextFromNthPreviousWord("a b c d e", spacingAndPunctuations, 1) + .extractPrevWordsContext().startsWith("<S>")); + } +} diff --git a/tests/src/com/android/inputmethod/latin/PunctuationTests.java b/tests/src/com/android/inputmethod/latin/PunctuationTests.java index 64750fbda..3537918de 100644 --- a/tests/src/com/android/inputmethod/latin/PunctuationTests.java +++ b/tests/src/com/android/inputmethod/latin/PunctuationTests.java @@ -38,7 +38,7 @@ public class PunctuationTests extends InputTestsBase { try { mLatinIME.loadSettings(); type(WORD_TO_TYPE); - sleep(DELAY_TO_WAIT_FOR_UNDERLINE); + sleep(DELAY_TO_WAIT_FOR_UNDERLINE_MILLIS); runMessages(); assertTrue("type word then type space should display punctuation strip", mLatinIME.getSuggestedWordsForTest().isPunctuationSuggestions()); diff --git a/tests/src/com/android/inputmethod/latin/RichInputConnectionAndTextRangeTests.java b/tests/src/com/android/inputmethod/latin/RichInputConnectionAndTextRangeTests.java index f9d72269e..08779b9d3 100644 --- a/tests/src/com/android/inputmethod/latin/RichInputConnectionAndTextRangeTests.java +++ b/tests/src/com/android/inputmethod/latin/RichInputConnectionAndTextRangeTests.java @@ -30,12 +30,12 @@ import android.view.inputmethod.ExtractedTextRequest; import android.view.inputmethod.InputConnection; import android.view.inputmethod.InputConnectionWrapper; -import com.android.inputmethod.latin.PrevWordsInfo.WordInfo; +import com.android.inputmethod.latin.common.Constants; +import com.android.inputmethod.latin.common.StringUtils; import com.android.inputmethod.latin.settings.SpacingAndPunctuations; -import com.android.inputmethod.latin.utils.PrevWordsInfoUtils; +import com.android.inputmethod.latin.utils.NgramContextUtils; import com.android.inputmethod.latin.utils.RunInLocale; import com.android.inputmethod.latin.utils.ScriptUtils; -import com.android.inputmethod.latin.utils.StringUtils; import com.android.inputmethod.latin.utils.TextRange; import java.util.Locale; @@ -137,7 +137,7 @@ public class RichInputConnectionAndTextRangeTests extends AndroidTestCase { } } - private class MockInputMethodService extends InputMethodService { + static class MockInputMethodService extends InputMethodService { private MockConnection mMockConnection; public void setInputConnection(final MockConnection mockConnection) { mMockConnection = mockConnection; @@ -158,26 +158,25 @@ public class RichInputConnectionAndTextRangeTests extends AndroidTestCase { */ public void testGetPreviousWord() { // If one of the following cases breaks, the bigram suggestions won't work. - assertEquals(PrevWordsInfoUtils.getPrevWordsInfoFromNthPreviousWord( - "abc def", mSpacingAndPunctuations, 2).mPrevWordsInfo[0].mWord, "abc"); - assertEquals(PrevWordsInfoUtils.getPrevWordsInfoFromNthPreviousWord( - "abc", mSpacingAndPunctuations, 2), PrevWordsInfo.BEGINNING_OF_SENTENCE); - assertEquals(PrevWordsInfoUtils.getPrevWordsInfoFromNthPreviousWord( - "abc. def", mSpacingAndPunctuations, 2), PrevWordsInfo.BEGINNING_OF_SENTENCE); - - assertFalse(PrevWordsInfoUtils.getPrevWordsInfoFromNthPreviousWord( - "abc def", mSpacingAndPunctuations, 2).mPrevWordsInfo[0].mIsBeginningOfSentence); - assertTrue(PrevWordsInfoUtils.getPrevWordsInfoFromNthPreviousWord( - "abc", mSpacingAndPunctuations, 2).mPrevWordsInfo[0].mIsBeginningOfSentence); + assertEquals(NgramContextUtils.getNgramContextFromNthPreviousWord( + "abc def", mSpacingAndPunctuations, 2).getNthPrevWord(1), "abc"); + assertEquals(NgramContextUtils.getNgramContextFromNthPreviousWord( + "abc", mSpacingAndPunctuations, 2), NgramContext.BEGINNING_OF_SENTENCE); + assertEquals(NgramContextUtils.getNgramContextFromNthPreviousWord( + "abc. def", mSpacingAndPunctuations, 2), NgramContext.BEGINNING_OF_SENTENCE); + + assertFalse(NgramContextUtils.getNgramContextFromNthPreviousWord( + "abc def", mSpacingAndPunctuations, 2).isBeginningOfSentenceContext()); + assertTrue(NgramContextUtils.getNgramContextFromNthPreviousWord( + "abc", mSpacingAndPunctuations, 2).isBeginningOfSentenceContext()); // For n-gram - assertEquals(PrevWordsInfoUtils.getPrevWordsInfoFromNthPreviousWord( - "abc def", mSpacingAndPunctuations, 1).mPrevWordsInfo[0].mWord, "def"); - assertEquals(PrevWordsInfoUtils.getPrevWordsInfoFromNthPreviousWord( - "abc def", mSpacingAndPunctuations, 1).mPrevWordsInfo[1].mWord, "abc"); - assertEquals(PrevWordsInfoUtils.getPrevWordsInfoFromNthPreviousWord( - "abc def", mSpacingAndPunctuations, 2).mPrevWordsInfo[1], - WordInfo.BEGINNING_OF_SENTENCE); + assertEquals(NgramContextUtils.getNgramContextFromNthPreviousWord( + "abc def", mSpacingAndPunctuations, 1).getNthPrevWord(1), "def"); + assertEquals(NgramContextUtils.getNgramContextFromNthPreviousWord( + "abc def", mSpacingAndPunctuations, 1).getNthPrevWord(2), "abc"); + assertTrue(NgramContextUtils.getNgramContextFromNthPreviousWord( + "abc def", mSpacingAndPunctuations, 2).isNthPrevWordBeginningOfSentence(2)); // The following tests reflect the current behavior of the function // RichInputConnection#getNthPreviousWord. @@ -186,33 +185,37 @@ public class RichInputConnectionAndTextRangeTests extends AndroidTestCase { // this function if needed - especially since it does not seem very // logical. These tests are just there to catch any unintentional // changes in the behavior of the RichInputConnection#getPreviousWord method. - assertEquals(PrevWordsInfoUtils.getPrevWordsInfoFromNthPreviousWord( - "abc def ", mSpacingAndPunctuations, 2).mPrevWordsInfo[0].mWord, "abc"); - assertEquals(PrevWordsInfoUtils.getPrevWordsInfoFromNthPreviousWord( - "abc def.", mSpacingAndPunctuations, 2).mPrevWordsInfo[0].mWord, "abc"); - assertEquals(PrevWordsInfoUtils.getPrevWordsInfoFromNthPreviousWord( - "abc def .", mSpacingAndPunctuations, 2).mPrevWordsInfo[0].mWord, "def"); - assertEquals(PrevWordsInfoUtils.getPrevWordsInfoFromNthPreviousWord( - "abc ", mSpacingAndPunctuations, 2), PrevWordsInfo.BEGINNING_OF_SENTENCE); - - assertEquals(PrevWordsInfoUtils.getPrevWordsInfoFromNthPreviousWord( - "abc def", mSpacingAndPunctuations, 1).mPrevWordsInfo[0].mWord, "def"); - assertEquals(PrevWordsInfoUtils.getPrevWordsInfoFromNthPreviousWord( - "abc def ", mSpacingAndPunctuations, 1).mPrevWordsInfo[0].mWord, "def"); - assertEquals(PrevWordsInfoUtils.getPrevWordsInfoFromNthPreviousWord( - "abc 'def", mSpacingAndPunctuations, 1).mPrevWordsInfo[0].mWord, "'def"); - assertEquals(PrevWordsInfoUtils.getPrevWordsInfoFromNthPreviousWord( - "abc def.", mSpacingAndPunctuations, 1), PrevWordsInfo.BEGINNING_OF_SENTENCE); - assertEquals(PrevWordsInfoUtils.getPrevWordsInfoFromNthPreviousWord( - "abc def .", mSpacingAndPunctuations, 1), PrevWordsInfo.BEGINNING_OF_SENTENCE); - assertEquals(PrevWordsInfoUtils.getPrevWordsInfoFromNthPreviousWord( - "abc, def", mSpacingAndPunctuations, 2), PrevWordsInfo.EMPTY_PREV_WORDS_INFO); - assertEquals(PrevWordsInfoUtils.getPrevWordsInfoFromNthPreviousWord( - "abc? def", mSpacingAndPunctuations, 2), PrevWordsInfo.EMPTY_PREV_WORDS_INFO); - assertEquals(PrevWordsInfoUtils.getPrevWordsInfoFromNthPreviousWord( - "abc! def", mSpacingAndPunctuations, 2), PrevWordsInfo.EMPTY_PREV_WORDS_INFO); - assertEquals(PrevWordsInfoUtils.getPrevWordsInfoFromNthPreviousWord( - "abc 'def", mSpacingAndPunctuations, 2), PrevWordsInfo.EMPTY_PREV_WORDS_INFO); + assertEquals(NgramContextUtils.getNgramContextFromNthPreviousWord( + "abc def ", mSpacingAndPunctuations, 2).getNthPrevWord(1), "abc"); + assertEquals(NgramContextUtils.getNgramContextFromNthPreviousWord( + "abc def.", mSpacingAndPunctuations, 2).getNthPrevWord(1), "abc"); + assertEquals(NgramContextUtils.getNgramContextFromNthPreviousWord( + "abc def .", mSpacingAndPunctuations, 2).getNthPrevWord(1), "def"); + assertTrue(NgramContextUtils.getNgramContextFromNthPreviousWord( + "abc ", mSpacingAndPunctuations, 2).isBeginningOfSentenceContext()); + + assertEquals(NgramContextUtils.getNgramContextFromNthPreviousWord( + "abc def", mSpacingAndPunctuations, 1).getNthPrevWord(1), "def"); + assertEquals(NgramContextUtils.getNgramContextFromNthPreviousWord( + "abc def ", mSpacingAndPunctuations, 1).getNthPrevWord(1), "def"); + assertEquals(NgramContextUtils.getNgramContextFromNthPreviousWord( + "abc 'def", mSpacingAndPunctuations, 1).getNthPrevWord(1), "'def"); + assertEquals(NgramContextUtils.getNgramContextFromNthPreviousWord( + "abc def.", mSpacingAndPunctuations, 1), NgramContext.BEGINNING_OF_SENTENCE); + assertEquals(NgramContextUtils.getNgramContextFromNthPreviousWord( + "abc def .", mSpacingAndPunctuations, 1), NgramContext.BEGINNING_OF_SENTENCE); + assertEquals(NgramContextUtils.getNgramContextFromNthPreviousWord( + "abc, def", mSpacingAndPunctuations, 2), NgramContext.EMPTY_PREV_WORDS_INFO); + // question mark is treated as the end of the sentence. Hence, beginning of the + // sentence is expected. + assertEquals(NgramContextUtils.getNgramContextFromNthPreviousWord( + "abc? def", mSpacingAndPunctuations, 2), NgramContext.BEGINNING_OF_SENTENCE); + // Exclamation mark is treated as the end of the sentence. Hence, beginning of the + // sentence is expected. + assertEquals(NgramContextUtils.getNgramContextFromNthPreviousWord( + "abc! def", mSpacingAndPunctuations, 2), NgramContext.BEGINNING_OF_SENTENCE); + assertEquals(NgramContextUtils.getNgramContextFromNthPreviousWord( + "abc 'def", mSpacingAndPunctuations, 2), NgramContext.EMPTY_PREV_WORDS_INFO); } public void testGetWordRangeAtCursor() { @@ -223,7 +226,6 @@ public class RichInputConnectionAndTextRangeTests extends AndroidTestCase { mSpacingAndPunctuations, new int[] { Constants.CODE_SPACE }); final SpacingAndPunctuations TAB = new SpacingAndPunctuations( mSpacingAndPunctuations, new int[] { Constants.CODE_TAB }); - final int[] SPACE_TAB = StringUtils.toSortedCodePointArray(" \t"); // A character that needs surrogate pair to represent its code point (U+2008A). final String SUPPLEMENTARY_CHAR_STRING = "\uD840\uDC8A"; final SpacingAndPunctuations SUPPLEMENTARY_CHAR = new SpacingAndPunctuations( diff --git a/tests/src/com/android/inputmethod/latin/RichInputMethodSubtypeTests.java b/tests/src/com/android/inputmethod/latin/RichInputMethodSubtypeTests.java new file mode 100644 index 000000000..af94be664 --- /dev/null +++ b/tests/src/com/android/inputmethod/latin/RichInputMethodSubtypeTests.java @@ -0,0 +1,318 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.inputmethod.latin; + +import android.content.Context; +import android.content.res.Resources; +import android.test.AndroidTestCase; +import android.test.suitebuilder.annotation.SmallTest; +import android.view.inputmethod.InputMethodInfo; +import android.view.inputmethod.InputMethodSubtype; + +import com.android.inputmethod.latin.R; +import com.android.inputmethod.latin.RichInputMethodManager; +import com.android.inputmethod.latin.RichInputMethodSubtype; +import com.android.inputmethod.latin.utils.AdditionalSubtypeUtils; +import com.android.inputmethod.latin.utils.RunInLocale; +import com.android.inputmethod.latin.utils.SubtypeLocaleUtils; + +import java.util.ArrayList; +import java.util.Locale; + +@SmallTest +public class RichInputMethodSubtypeTests extends AndroidTestCase { + // All input method subtypes of LatinIME. + private final ArrayList<RichInputMethodSubtype> mSubtypesList = new ArrayList<>(); + + private RichInputMethodManager mRichImm; + private Resources mRes; + private InputMethodSubtype mSavedAddtionalSubtypes[]; + + RichInputMethodSubtype EN_US; + RichInputMethodSubtype EN_GB; + RichInputMethodSubtype ES_US; + RichInputMethodSubtype FR; + RichInputMethodSubtype FR_CA; + RichInputMethodSubtype FR_CH; + RichInputMethodSubtype DE; + RichInputMethodSubtype DE_CH; + RichInputMethodSubtype HI; + RichInputMethodSubtype SR; + RichInputMethodSubtype ZZ; + RichInputMethodSubtype DE_QWERTY; + RichInputMethodSubtype FR_QWERTZ; + RichInputMethodSubtype EN_US_AZERTY; + RichInputMethodSubtype EN_UK_DVORAK; + RichInputMethodSubtype ES_US_COLEMAK; + RichInputMethodSubtype ZZ_AZERTY; + RichInputMethodSubtype ZZ_PC; + + // These are preliminary subtypes and may not exist. + RichInputMethodSubtype HI_LATN; // Hinglish + RichInputMethodSubtype SR_LATN; // Serbian Latin + RichInputMethodSubtype HI_LATN_DVORAK; + RichInputMethodSubtype SR_LATN_QWERTY; + + @Override + protected void setUp() throws Exception { + super.setUp(); + final Context context = getContext(); + mRes = context.getResources(); + RichInputMethodManager.init(context); + mRichImm = RichInputMethodManager.getInstance(); + + // Save and reset additional subtypes + mSavedAddtionalSubtypes = mRichImm.getAdditionalSubtypes(); + final InputMethodSubtype[] predefinedAddtionalSubtypes = + AdditionalSubtypeUtils.createAdditionalSubtypesArray( + AdditionalSubtypeUtils.createPrefSubtypes( + mRes.getStringArray(R.array.predefined_subtypes))); + mRichImm.setAdditionalInputMethodSubtypes(predefinedAddtionalSubtypes); + + final InputMethodInfo imi = mRichImm.getInputMethodInfoOfThisIme(); + final int subtypeCount = imi.getSubtypeCount(); + for (int index = 0; index < subtypeCount; index++) { + final InputMethodSubtype subtype = imi.getSubtypeAt(index); + mSubtypesList.add(new RichInputMethodSubtype(subtype)); + } + + EN_US = new RichInputMethodSubtype(mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet( + Locale.US.toString(), "qwerty")); + EN_GB = new RichInputMethodSubtype(mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet( + Locale.UK.toString(), "qwerty")); + ES_US = new RichInputMethodSubtype(mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet( + "es_US", "spanish")); + FR = new RichInputMethodSubtype(mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet( + Locale.FRENCH.toString(), "azerty")); + FR_CA = new RichInputMethodSubtype(mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet( + Locale.CANADA_FRENCH.toString(), "qwerty")); + FR_CH = new RichInputMethodSubtype(mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet( + "fr_CH", "swiss")); + DE = new RichInputMethodSubtype(mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet( + Locale.GERMAN.toString(), "qwertz")); + DE_CH = new RichInputMethodSubtype(mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet( + "de_CH", "swiss")); + HI = new RichInputMethodSubtype(mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet( + "hi", "hindi")); + SR = new RichInputMethodSubtype(mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet( + "sr", "south_slavic")); + ZZ = new RichInputMethodSubtype(mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet( + SubtypeLocaleUtils.NO_LANGUAGE, "qwerty")); + DE_QWERTY = new RichInputMethodSubtype( + AdditionalSubtypeUtils.createAsciiEmojiCapableAdditionalSubtype( + Locale.GERMAN.toString(), "qwerty")); + FR_QWERTZ = new RichInputMethodSubtype( + AdditionalSubtypeUtils.createAsciiEmojiCapableAdditionalSubtype( + Locale.FRENCH.toString(), "qwertz")); + EN_US_AZERTY = new RichInputMethodSubtype( + AdditionalSubtypeUtils.createAsciiEmojiCapableAdditionalSubtype( + Locale.US.toString(), "azerty")); + EN_UK_DVORAK = new RichInputMethodSubtype( + AdditionalSubtypeUtils.createAsciiEmojiCapableAdditionalSubtype( + Locale.UK.toString(), "dvorak")); + ES_US_COLEMAK = new RichInputMethodSubtype( + AdditionalSubtypeUtils.createAsciiEmojiCapableAdditionalSubtype( + "es_US", "colemak")); + ZZ_AZERTY = new RichInputMethodSubtype( + AdditionalSubtypeUtils.createAsciiEmojiCapableAdditionalSubtype( + SubtypeLocaleUtils.NO_LANGUAGE, "azerty")); + ZZ_PC = new RichInputMethodSubtype( + AdditionalSubtypeUtils.createAsciiEmojiCapableAdditionalSubtype( + SubtypeLocaleUtils.NO_LANGUAGE, "pcqwerty")); + + final InputMethodSubtype hiLatn = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet( + "hi_ZZ", "qwerty"); + if (hiLatn != null) { + HI_LATN = new RichInputMethodSubtype(hiLatn); + HI_LATN_DVORAK = new RichInputMethodSubtype( + AdditionalSubtypeUtils.createAsciiEmojiCapableAdditionalSubtype( + "hi_ZZ", "dvorak")); + } + final InputMethodSubtype srLatn = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet( + "sr_ZZ", "serbian_qwertz"); + if (srLatn != null) { + SR_LATN = new RichInputMethodSubtype(srLatn); + SR_LATN_QWERTY = new RichInputMethodSubtype( + AdditionalSubtypeUtils.createAsciiEmojiCapableAdditionalSubtype( + "sr_ZZ", "qwerty")); + } + } + + @Override + protected void tearDown() throws Exception { + // Restore additional subtypes. + mRichImm.setAdditionalInputMethodSubtypes(mSavedAddtionalSubtypes); + super.tearDown(); + } + + public void testAllFullDisplayNameForSpacebar() { + for (final RichInputMethodSubtype subtype : mSubtypesList) { + final String subtypeName = SubtypeLocaleUtils + .getSubtypeDisplayNameInSystemLocale(subtype.getRawSubtype()); + final String spacebarText = subtype.getFullDisplayName(); + final String languageName = SubtypeLocaleUtils + .getSubtypeLocaleDisplayName(subtype.getLocale().toString()); + if (subtype.isNoLanguage()) { + assertFalse(subtypeName, spacebarText.contains(languageName)); + } else { + assertTrue(subtypeName, spacebarText.contains(languageName)); + } + } + } + + public void testAllMiddleDisplayNameForSpacebar() { + for (final RichInputMethodSubtype subtype : mSubtypesList) { + final String subtypeName = SubtypeLocaleUtils + .getSubtypeDisplayNameInSystemLocale(subtype.getRawSubtype()); + final Locale locale = subtype.getLocale(); + final Locale displayLocale = SubtypeLocaleUtils.getDisplayLocaleOfSubtypeLocale( + locale.toString()); + if (Locale.ROOT.equals(displayLocale)) { + // Skip test because the language part of this locale string doesn't represent + // the locale to be displayed on the spacebar (for example Hinglish). + continue; + } + final String spacebarText = subtype.getMiddleDisplayName(); + if (subtype.isNoLanguage()) { + assertEquals(subtypeName, SubtypeLocaleUtils.getKeyboardLayoutSetDisplayName( + subtype.getRawSubtype()), spacebarText); + } else { + assertEquals(subtypeName, + SubtypeLocaleUtils.getSubtypeLanguageDisplayName(locale.toString()), + spacebarText); + } + } + } + + // InputMethodSubtype's display name for spacebar text in its locale. + // isAdditionalSubtype (T=true, F=false) + // locale layout | Middle Full + // ------ -------------- - --------- ---------------------- + // en_US qwerty F English English (US) exception + // en_GB qwerty F English English (UK) exception + // es_US spanish F Español Español (EE.UU.) exception + // fr azerty F Français Français + // fr_CA qwerty F Français Français (Canada) + // fr_CH swiss F Français Français (Suisse) + // de qwertz F Deutsch Deutsch + // de_CH swiss F Deutsch Deutsch (Schweiz) + // hi hindi F हिन्दी हिन्दी + // hi_ZZ qwerty F Hinglish Hinglish exception + // sr south_slavic F Српски Српски + // sr_ZZ serbian_qwertz F Srpski Srpski exception + // zz qwerty F QWERTY QWERTY + // fr qwertz T Français Français + // de qwerty T Deutsch Deutsch + // en_US azerty T English English (US) + // en_GB dvorak T English English (UK) + // hi_ZZ dvorak T Hinglish Hinglish exception + // sr_ZZ qwerty T Srpski Srpski exception + // 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)", EN_US.getFullDisplayName()); + assertEquals("en_GB", "English (UK)", EN_GB.getFullDisplayName()); + assertEquals("es_US", "Español (EE.UU.)", ES_US.getFullDisplayName()); + assertEquals("fr", "Français", FR.getFullDisplayName()); + assertEquals("fr_CA", "Français (Canada)", FR_CA.getFullDisplayName()); + assertEquals("fr_CH", "Français (Suisse)", FR_CH.getFullDisplayName()); + assertEquals("de", "Deutsch", DE.getFullDisplayName()); + assertEquals("de_CH", "Deutsch (Schweiz)", DE_CH.getFullDisplayName()); + assertEquals("hi", "हिन्दी", HI.getFullDisplayName()); + assertEquals("sr", "Српски", SR.getFullDisplayName()); + assertEquals("zz", "QWERTY", ZZ.getFullDisplayName()); + + assertEquals("en_US", "English", EN_US.getMiddleDisplayName()); + assertEquals("en_GB", "English", EN_GB.getMiddleDisplayName()); + assertEquals("es_US", "Español", ES_US.getMiddleDisplayName()); + assertEquals("fr", "Français", FR.getMiddleDisplayName()); + assertEquals("fr_CA", "Français", FR_CA.getMiddleDisplayName()); + assertEquals("fr_CH", "Français", FR_CH.getMiddleDisplayName()); + assertEquals("de", "Deutsch", DE.getMiddleDisplayName()); + assertEquals("de_CH", "Deutsch", DE_CH.getMiddleDisplayName()); + assertEquals("zz", "QWERTY", ZZ.getMiddleDisplayName()); + + // These are preliminary subtypes and may not exist. + if (HI_LATN != null) { + assertEquals("hi_ZZ", "Hinglish", HI_LATN.getFullDisplayName()); + assertEquals("hi_ZZ", "Hinglish", HI_LATN.getMiddleDisplayName()); + } + if (SR_LATN != null) { + assertEquals("sr_ZZ", "Srpski", SR_LATN.getFullDisplayName()); + assertEquals("sr_ZZ", "Srpski", SR_LATN.getMiddleDisplayName()); + } + return null; + } + }; + + private final RunInLocale<Void> testsAdditionalSubtypesForSpacebar = new RunInLocale<Void>() { + @Override + protected Void job(final Resources res) { + assertEquals("fr qwertz", "Français", FR_QWERTZ.getFullDisplayName()); + assertEquals("de qwerty", "Deutsch", DE_QWERTY.getFullDisplayName()); + assertEquals("en_US azerty", "English (US)", EN_US_AZERTY.getFullDisplayName()); + assertEquals("en_UK dvorak", "English (UK)", EN_UK_DVORAK.getFullDisplayName()); + assertEquals("es_US colemak", "Español (EE.UU.)", ES_US_COLEMAK.getFullDisplayName()); + assertEquals("zz azerty", "AZERTY", ZZ_AZERTY.getFullDisplayName()); + assertEquals("zz pc", "PC", ZZ_PC.getFullDisplayName()); + + assertEquals("fr qwertz", "Français", FR_QWERTZ.getMiddleDisplayName()); + assertEquals("de qwerty", "Deutsch", DE_QWERTY.getMiddleDisplayName()); + assertEquals("en_US azerty", "English", EN_US_AZERTY.getMiddleDisplayName()); + assertEquals("en_UK dvorak", "English", EN_UK_DVORAK.getMiddleDisplayName()); + assertEquals("es_US colemak", "Español", ES_US_COLEMAK.getMiddleDisplayName()); + assertEquals("zz azerty", "AZERTY", ZZ_AZERTY.getMiddleDisplayName()); + assertEquals("zz pc", "PC", ZZ_PC.getMiddleDisplayName()); + + // These are preliminary subtypes and may not exist. + if (HI_LATN_DVORAK != null) { + assertEquals("hi_ZZ dvorak", "Hinglish", HI_LATN_DVORAK.getFullDisplayName()); + assertEquals("hi_ZZ dvorak", "Hinglish", HI_LATN_DVORAK.getMiddleDisplayName()); + } + if (SR_LATN_QWERTY != null) { + assertEquals("sr_ZZ qwerty", "Srpski", SR_LATN_QWERTY.getFullDisplayName()); + assertEquals("sr_ZZ qwerty", "Srpski", SR_LATN_QWERTY.getMiddleDisplayName()); + } + 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); + } + + public void testRichInputMethodSubtypeForNullInputMethodSubtype() { + RichInputMethodSubtype subtype = RichInputMethodSubtype.getRichInputMethodSubtype(null); + assertNotNull(subtype); + assertEquals("zz", subtype.getRawSubtype().getLocale()); + assertEquals("keyboard", subtype.getRawSubtype().getMode()); + } +} diff --git a/tests/src/com/android/inputmethod/latin/ShiftModeTests.java b/tests/src/com/android/inputmethod/latin/ShiftModeTests.java index db3c9baa9..59bb5f8a4 100644 --- a/tests/src/com/android/inputmethod/latin/ShiftModeTests.java +++ b/tests/src/com/android/inputmethod/latin/ShiftModeTests.java @@ -16,13 +16,11 @@ package com.android.inputmethod.latin; -import android.os.Build; import android.test.suitebuilder.annotation.LargeTest; import android.text.TextUtils; import android.view.inputmethod.EditorInfo; -import com.android.inputmethod.latin.Constants; -import com.android.inputmethod.latin.WordComposer; +import com.android.inputmethod.latin.common.Constants; @LargeTest public class ShiftModeTests extends InputTestsBase { @@ -75,7 +73,7 @@ public class ShiftModeTests extends InputTestsBase { repeatKey(Constants.CODE_DELETE); } assertFalse("Caps immediately after repeating Backspace a lot", isCapsModeAutoShifted()); - sleep(DELAY_TO_WAIT_FOR_PREDICTIONS); + sleep(DELAY_TO_WAIT_FOR_PREDICTIONS_MILLIS); runMessages(); assertTrue("Caps after a while after repeating Backspace a lot", isCapsModeAutoShifted()); } diff --git a/tests/src/com/android/inputmethod/latin/SuggestedWordsTests.java b/tests/src/com/android/inputmethod/latin/SuggestedWordsTests.java index 869c550e0..d465ce674 100644 --- a/tests/src/com/android/inputmethod/latin/SuggestedWordsTests.java +++ b/tests/src/com/android/inputmethod/latin/SuggestedWordsTests.java @@ -37,7 +37,7 @@ public class SuggestedWordsTests extends AndroidTestCase { private static SuggestedWordInfo createTypedWordInfo(final String word) { // Use 100 as the frequency because the numerical value does not matter as // long as it's > 1 and < INT_MAX. - return new SuggestedWordInfo(word, 100 /* score */, + return new SuggestedWordInfo(word, "" /* prevWordsContext */, 100 /* score */, SuggestedWordInfo.KIND_TYPED, null /* sourceDict */, SuggestedWordInfo.NOT_AN_INDEX /* indexOfTouchPointOfSecondWord */, @@ -52,49 +52,23 @@ public class SuggestedWordsTests extends AndroidTestCase { * @return a new instance of {@link SuggestedWordInfo}. */ private static SuggestedWordInfo createCorrectionWordInfo(final String word) { - return new SuggestedWordInfo(word, 1 /* score */, + return new SuggestedWordInfo(word, "" /* prevWordsContext */, 1 /* score */, SuggestedWordInfo.KIND_CORRECTION, null /* sourceDict */, SuggestedWordInfo.NOT_AN_INDEX /* indexOfTouchPointOfSecondWord */, SuggestedWordInfo.NOT_A_CONFIDENCE /* autoCommitFirstWordConfidence */); } - public void testGetSuggestedWordsExcludingTypedWord() { - final String TYPED_WORD = "typed"; - final int NUMBER_OF_ADDED_SUGGESTIONS = 5; - final int KIND_OF_SECOND_CORRECTION = SuggestedWordInfo.KIND_CORRECTION; - final ArrayList<SuggestedWordInfo> list = new ArrayList<>(); - list.add(createTypedWordInfo(TYPED_WORD)); - for (int i = 0; i < NUMBER_OF_ADDED_SUGGESTIONS; ++i) { - list.add(createCorrectionWordInfo(Integer.toString(i))); + private static ArrayList<SuggestedWordInfo> createCorrectionWordInfos(final String... words) { + final ArrayList<SuggestedWordInfo> infos = new ArrayList<>(); + for (final String word : words) { + infos.add(createCorrectionWordInfo(word)); } - - final SuggestedWords words = new SuggestedWords( - list, null /* rawSuggestions */, - false /* typedWordValid */, - false /* willAutoCorrect */, - false /* isObsoleteSuggestions */, - SuggestedWords.INPUT_STYLE_NONE); - assertEquals(NUMBER_OF_ADDED_SUGGESTIONS + 1, words.size()); - assertEquals("typed", words.getWord(0)); - assertTrue(words.getInfo(0).isKindOf(SuggestedWordInfo.KIND_TYPED)); - assertEquals("0", words.getWord(1)); - assertTrue(words.getInfo(1).isKindOf(KIND_OF_SECOND_CORRECTION)); - assertEquals("4", words.getWord(5)); - assertTrue(words.getInfo(5).isKindOf(KIND_OF_SECOND_CORRECTION)); - - final SuggestedWords wordsWithoutTyped = - words.getSuggestedWordsExcludingTypedWordForRecorrection(); - // Make sure that the typed word has indeed been excluded, by testing the size of the - // suggested words, the string and the kind of the top suggestion, which should match - // the string and kind of what we inserted after the typed word. - assertEquals(words.size() - 1, wordsWithoutTyped.size()); - assertEquals("0", wordsWithoutTyped.getWord(0)); - assertTrue(wordsWithoutTyped.getInfo(0).isKindOf(KIND_OF_SECOND_CORRECTION)); + return infos; } // Helper for testGetTransformedWordInfo - private SuggestedWordInfo transformWordInfo(final String info, + private static SuggestedWordInfo transformWordInfo(final String info, final int trailingSingleQuotesCount) { final SuggestedWordInfo suggestedWordInfo = createTypedWordInfo(info); final SuggestedWordInfo returnedWordInfo = @@ -106,6 +80,30 @@ public class SuggestedWordsTests extends AndroidTestCase { return returnedWordInfo; } + public void testRemoveDupesNoDupes() { + final ArrayList<SuggestedWordInfo> infos = createCorrectionWordInfos("a", "c"); + assertEquals(-1, SuggestedWordInfo.removeDups("b", infos)); + assertEquals(2, infos.size()); + } + + public void testRemoveDupesTypedWordNotDupe() { + final ArrayList<SuggestedWordInfo> infos = createCorrectionWordInfos("a", "a", "c"); + assertEquals(-1, SuggestedWordInfo.removeDups("b", infos)); + assertEquals(2, infos.size()); + } + + public void testRemoveDupesTypedWordOnlyDupe() { + final ArrayList<SuggestedWordInfo> infos = createCorrectionWordInfos("a", "b", "c"); + assertEquals(1, SuggestedWordInfo.removeDups("b", infos)); + assertEquals(2, infos.size()); + } + + public void testRemoveDupesTypedWordNotOnlyDupe() { + final ArrayList<SuggestedWordInfo> infos = createCorrectionWordInfos("a", "b", "b", "c"); + assertEquals(1, SuggestedWordInfo.removeDups("b", infos)); + assertEquals(2, infos.size()); + } + public void testGetTransformedSuggestedWordInfo() { SuggestedWordInfo result = transformWordInfo("word", 0); assertEquals(result.mWord, "word"); @@ -123,9 +121,10 @@ public class SuggestedWordsTests extends AndroidTestCase { public void testGetTypedWordInfoOrNull() { final String TYPED_WORD = "typed"; + final SuggestedWordInfo TYPED_WORD_INFO = createTypedWordInfo(TYPED_WORD); final int NUMBER_OF_ADDED_SUGGESTIONS = 5; final ArrayList<SuggestedWordInfo> list = new ArrayList<>(); - list.add(createTypedWordInfo(TYPED_WORD)); + list.add(TYPED_WORD_INFO); for (int i = 0; i < NUMBER_OF_ADDED_SUGGESTIONS; ++i) { list.add(createCorrectionWordInfo(Integer.toString(i))); } @@ -133,30 +132,41 @@ public class SuggestedWordsTests extends AndroidTestCase { // Make sure getTypedWordInfoOrNull() returns non-null object. final SuggestedWords wordsWithTypedWord = new SuggestedWords( list, null /* rawSuggestions */, + TYPED_WORD_INFO, false /* typedWordValid */, false /* willAutoCorrect */, false /* isObsoleteSuggestions */, - SuggestedWords.INPUT_STYLE_NONE); + SuggestedWords.INPUT_STYLE_NONE, + SuggestedWords.NOT_A_SEQUENCE_NUMBER); final SuggestedWordInfo typedWord = wordsWithTypedWord.getTypedWordInfoOrNull(); assertNotNull(typedWord); assertEquals(TYPED_WORD, typedWord.mWord); - // Make sure getTypedWordInfoOrNull() returns null. - final SuggestedWords wordsWithoutTypedWord = - wordsWithTypedWord.getSuggestedWordsExcludingTypedWordForRecorrection(); + // Make sure getTypedWordInfoOrNull() returns null when no typed word. + list.remove(0); + final SuggestedWords wordsWithoutTypedWord = new SuggestedWords( + list, null /* rawSuggestions */, + null /* typedWord */, + false /* typedWordValid */, + false /* willAutoCorrect */, + false /* isObsoleteSuggestions */, + SuggestedWords.INPUT_STYLE_NONE, + SuggestedWords.NOT_A_SEQUENCE_NUMBER); assertNull(wordsWithoutTypedWord.getTypedWordInfoOrNull()); // Make sure getTypedWordInfoOrNull() returns null. - assertNull(SuggestedWords.EMPTY.getTypedWordInfoOrNull()); + assertNull(SuggestedWords.getEmptyInstance().getTypedWordInfoOrNull()); final SuggestedWords emptySuggestedWords = new SuggestedWords( new ArrayList<SuggestedWordInfo>(), null /* rawSuggestions */, + null /* typedWord */, false /* typedWordValid */, false /* willAutoCorrect */, false /* isObsoleteSuggestions */, - SuggestedWords.INPUT_STYLE_NONE); + SuggestedWords.INPUT_STYLE_NONE, + SuggestedWords.NOT_A_SEQUENCE_NUMBER); assertNull(emptySuggestedWords.getTypedWordInfoOrNull()); - assertNull(SuggestedWords.EMPTY.getTypedWordInfoOrNull()); + assertNull(SuggestedWords.getEmptyInstance().getTypedWordInfoOrNull()); } } diff --git a/tests/src/com/android/inputmethod/latin/WordComposerTests.java b/tests/src/com/android/inputmethod/latin/WordComposerTests.java index c44544f3d..8ae475f7e 100644 --- a/tests/src/com/android/inputmethod/latin/WordComposerTests.java +++ b/tests/src/com/android/inputmethod/latin/WordComposerTests.java @@ -19,8 +19,9 @@ package com.android.inputmethod.latin; import android.test.AndroidTestCase; import android.test.suitebuilder.annotation.SmallTest; -import com.android.inputmethod.latin.utils.CoordinateUtils; -import com.android.inputmethod.latin.utils.StringUtils; +import com.android.inputmethod.latin.common.Constants; +import com.android.inputmethod.latin.common.CoordinateUtils; +import com.android.inputmethod.latin.common.StringUtils; /** * Unit tests for WordComposer. diff --git a/tests/src/com/android/inputmethod/latin/accounts/AccountsChangedReceiverTests.java b/tests/src/com/android/inputmethod/latin/accounts/AccountsChangedReceiverTests.java new file mode 100644 index 000000000..832817967 --- /dev/null +++ b/tests/src/com/android/inputmethod/latin/accounts/AccountsChangedReceiverTests.java @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.inputmethod.latin.accounts; + +import android.accounts.AccountManager; +import android.content.Context; +import android.content.Intent; +import android.content.SharedPreferences; +import android.preference.PreferenceManager; +import android.test.AndroidTestCase; + +import com.android.inputmethod.latin.settings.LocalSettingsConstants; + +/** + * Tests for {@link AccountsChangedReceiver}. + */ +public class AccountsChangedReceiverTests extends AndroidTestCase { + private static final String ACCOUNT_1 = "account1@example.com"; + private static final String ACCOUNT_2 = "account2@example.com"; + + private SharedPreferences mPrefs; + private String mLastKnownAccount = null; + + @Override + protected void setUp() throws Exception { + super.setUp(); + mPrefs = PreferenceManager.getDefaultSharedPreferences(getContext()); + // Keep track of the current account so that we restore it when the test finishes. + mLastKnownAccount = mPrefs.getString(LocalSettingsConstants.PREF_ACCOUNT_NAME, null); + } + + @Override + protected void tearDown() throws Exception { + super.tearDown(); + // Restore the account that was present before running the test. + updateAccountName(mLastKnownAccount); + } + + public void testUnknownIntent() { + updateAccountName(ACCOUNT_1); + AccountsChangedReceiver reciever = new AccountsChangedReceiver(); + reciever.onReceive(getContext(), new Intent("some-random-action")); + // Account should *not* be removed from preferences. + assertAccountName(ACCOUNT_1); + } + + public void testAccountRemoved() { + updateAccountName(ACCOUNT_1); + AccountsChangedReceiver reciever = new AccountsChangedReceiver() { + @Override + protected String[] getAccountsForLogin(Context context) { + return new String[] {ACCOUNT_2}; + } + }; + reciever.onReceive(getContext(), new Intent(AccountManager.LOGIN_ACCOUNTS_CHANGED_ACTION)); + // Account should be removed from preferences. + assertAccountName(null); + } + + public void testAccountRemoved_noAccounts() { + updateAccountName(ACCOUNT_2); + AccountsChangedReceiver reciever = new AccountsChangedReceiver() { + @Override + protected String[] getAccountsForLogin(Context context) { + return new String[0]; + } + }; + reciever.onReceive(getContext(), new Intent(AccountManager.LOGIN_ACCOUNTS_CHANGED_ACTION)); + // Account should be removed from preferences. + assertAccountName(null); + } + + public void testAccountNotRemoved() { + updateAccountName(ACCOUNT_2); + AccountsChangedReceiver reciever = new AccountsChangedReceiver() { + @Override + protected String[] getAccountsForLogin(Context context) { + return new String[] {ACCOUNT_1, ACCOUNT_2}; + } + }; + reciever.onReceive(getContext(), new Intent(AccountManager.LOGIN_ACCOUNTS_CHANGED_ACTION)); + // Account should *not* be removed from preferences. + assertAccountName(ACCOUNT_2); + } + + private void updateAccountName(String accountName) { + if (accountName == null) { + mPrefs.edit().remove(LocalSettingsConstants.PREF_ACCOUNT_NAME).apply(); + } else { + mPrefs.edit().putString(LocalSettingsConstants.PREF_ACCOUNT_NAME, accountName).apply(); + } + } + + private void assertAccountName(String expectedAccountName) { + assertEquals(expectedAccountName, + mPrefs.getString(LocalSettingsConstants.PREF_ACCOUNT_NAME, null)); + } +} diff --git a/tests/src/com/android/inputmethod/latin/InputPointersTests.java b/tests/src/com/android/inputmethod/latin/common/InputPointersTests.java index 1a47cddf4..6b3490de8 100644 --- a/tests/src/com/android/inputmethod/latin/InputPointersTests.java +++ b/tests/src/com/android/inputmethod/latin/common/InputPointersTests.java @@ -14,13 +14,11 @@ * limitations under the License. */ -package com.android.inputmethod.latin; +package com.android.inputmethod.latin.common; import android.test.AndroidTestCase; import android.test.suitebuilder.annotation.SmallTest; -import com.android.inputmethod.latin.utils.ResizableIntArray; - import java.util.Arrays; @SmallTest diff --git a/tests/src/com/android/inputmethod/latin/utils/ResizableIntArrayTests.java b/tests/src/com/android/inputmethod/latin/common/ResizableIntArrayTests.java index 8f58e6873..bd1629faf 100644 --- a/tests/src/com/android/inputmethod/latin/utils/ResizableIntArrayTests.java +++ b/tests/src/com/android/inputmethod/latin/common/ResizableIntArrayTests.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.inputmethod.latin.utils; +package com.android.inputmethod.latin.common; import android.test.AndroidTestCase; import android.test.suitebuilder.annotation.SmallTest; @@ -79,13 +79,13 @@ public class ResizableIntArrayTests extends AndroidTestCase { public void testGet() { final ResizableIntArray src = new ResizableIntArray(DEFAULT_CAPACITY); try { - final int value = src.get(0); + src.get(0); fail("get(0) shouldn't succeed"); } catch (ArrayIndexOutOfBoundsException e) { // success } try { - final int value = src.get(DEFAULT_CAPACITY); + src.get(DEFAULT_CAPACITY); fail("get(DEFAULT_CAPACITY) shouldn't succeed"); } catch (ArrayIndexOutOfBoundsException e) { // success @@ -98,7 +98,7 @@ public class ResizableIntArrayTests extends AndroidTestCase { assertEquals("value after add at " + index, valueAddAt, src.get(index)); assertEquals("value after add at 0", 0, src.get(0)); try { - final int value = src.get(src.getLength()); + src.get(src.getLength()); fail("get(length) shouldn't succeed"); } catch (ArrayIndexOutOfBoundsException e) { // success diff --git a/tests/src/com/android/inputmethod/latin/utils/StringAndJsonUtilsTests.java b/tests/src/com/android/inputmethod/latin/common/StringUtilsTests.java index 637ae10ee..ec9d4be92 100644 --- a/tests/src/com/android/inputmethod/latin/utils/StringAndJsonUtilsTests.java +++ b/tests/src/com/android/inputmethod/latin/common/StringUtilsTests.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012 The Android Open Source Project + * Copyright (C) 2014 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,22 +14,177 @@ * limitations under the License. */ -package com.android.inputmethod.latin.utils; +package com.android.inputmethod.latin.common; import android.test.AndroidTestCase; import android.test.suitebuilder.annotation.SmallTest; -import android.text.SpannableString; -import android.text.Spanned; -import android.text.SpannedString; -import com.android.inputmethod.latin.Constants; - -import java.util.Arrays; -import java.util.List; import java.util.Locale; @SmallTest -public class StringAndJsonUtilsTests extends AndroidTestCase { +public class StringUtilsTests extends AndroidTestCase { + private static final Locale US = Locale.US; + private static final Locale GERMAN = Locale.GERMAN; + private static final Locale TURKEY = new Locale("tr", "TR"); + private static final Locale GREECE = new Locale("el", "GR"); + + private static void assert_toTitleCaseOfKeyLabel(final Locale locale, + final String lowerCase, final String expected) { + assertEquals(lowerCase + " in " + locale, expected, + StringUtils.toTitleCaseOfKeyLabel(lowerCase, locale)); + } + + public void test_toTitleCaseOfKeyLabel() { + assert_toTitleCaseOfKeyLabel(US, null, null); + assert_toTitleCaseOfKeyLabel(US, "", ""); + assert_toTitleCaseOfKeyLabel(US, "aeiou", "AEIOU"); + // U+00E0: "à" LATIN SMALL LETTER A WITH GRAVE + // U+00E8: "è" LATIN SMALL LETTER E WITH GRAVE + // U+00EE: "î" LATIN SMALL LETTER I WITH CIRCUMFLEX + // U+00F6: "ö" LATIN SMALL LETTER O WITH DIAERESIS + // U+016B: "ū" LATIN SMALL LETTER U WITH MACRON + // U+00F1: "ñ" LATIN SMALL LETTER N WITH TILDE + // U+00E7: "ç" LATIN SMALL LETTER C WITH CEDILLA + // U+00C0: "À" LATIN CAPITAL LETTER A WITH GRAVE + // U+00C8: "È" LATIN CAPITAL LETTER E WITH GRAVE + // U+00CE: "Î" LATIN CAPITAL LETTER I WITH CIRCUMFLEX + // U+00D6: "Ö" LATIN CAPITAL LETTER O WITH DIAERESIS + // U+016A: "Ū" LATIN CAPITAL LETTER U WITH MACRON + // U+00D1: "Ñ" LATIN CAPITAL LETTER N WITH TILDE + // U+00C7: "Ç" LATIN CAPITAL LETTER C WITH CEDILLA + assert_toTitleCaseOfKeyLabel(US, + "\u00E0\u00E8\u00EE\u00F6\u016B\u00F1\u00E7", + "\u00C0\u00C8\u00CE\u00D6\u016A\u00D1\u00C7"); + // U+00DF: "ß" LATIN SMALL LETTER SHARP S + // U+015B: "ś" LATIN SMALL LETTER S WITH ACUTE + // U+0161: "š" LATIN SMALL LETTER S WITH CARON + // U+015A: "Ś" LATIN CAPITAL LETTER S WITH ACUTE + // U+0160: "Š" LATIN CAPITAL LETTER S WITH CARONZ + assert_toTitleCaseOfKeyLabel(GERMAN, + "\u00DF\u015B\u0161", + "SS\u015A\u0160"); + // U+0259: "ə" LATIN SMALL LETTER SCHWA + // U+0069: "i" LATIN SMALL LETTER I + // U+0131: "ı" LATIN SMALL LETTER DOTLESS I + // U+018F: "Ə" LATIN SMALL LETTER SCHWA + // U+0130: "İ" LATIN SMALL LETTER I WITH DOT ABOVE + // U+0049: "I" LATIN SMALL LETTER I + assert_toTitleCaseOfKeyLabel(TURKEY, + "\u0259\u0069\u0131", + "\u018F\u0130\u0049"); + // U+03C3: "σ" GREEK SMALL LETTER SIGMA + // U+03C2: "ς" GREEK SMALL LETTER FINAL SIGMA + // U+03A3: "Σ" GREEK CAPITAL LETTER SIGMA + assert_toTitleCaseOfKeyLabel(GREECE, + "\u03C3\u03C2", + "\u03A3\u03A3"); + // U+03AC: "ά" GREEK SMALL LETTER ALPHA WITH TONOS + // U+03AD: "έ" GREEK SMALL LETTER EPSILON WITH TONOS + // U+03AE: "ή" GREEK SMALL LETTER ETA WITH TONOS + // U+03AF: "ί" GREEK SMALL LETTER IOTA WITH TONOS + // U+03CC: "ό" GREEK SMALL LETTER OMICRON WITH TONOS + // U+03CD: "ύ" GREEK SMALL LETTER UPSILON WITH TONOS + // U+03CE: "ώ" GREEK SMALL LETTER OMEGA WITH TONOS + // U+0386: "Ά" GREEK CAPITAL LETTER ALPHA WITH TONOS + // U+0388: "Έ" GREEK CAPITAL LETTER EPSILON WITH TONOS + // U+0389: "Ή" GREEK CAPITAL LETTER ETA WITH TONOS + // U+038A: "Ί" GREEK CAPITAL LETTER IOTA WITH TONOS + // U+038C: "Ό" GREEK CAPITAL LETTER OMICRON WITH TONOS + // U+038E: "Ύ" GREEK CAPITAL LETTER UPSILON WITH TONOS + // U+038F: "Ώ" GREEK CAPITAL LETTER OMEGA WITH TONOS + assert_toTitleCaseOfKeyLabel(GREECE, + "\u03AC\u03AD\u03AE\u03AF\u03CC\u03CD\u03CE", + "\u0386\u0388\u0389\u038A\u038C\u038E\u038F"); + // U+03CA: "ϊ" GREEK SMALL LETTER IOTA WITH DIALYTIKA + // U+03CB: "ϋ" GREEK SMALL LETTER UPSILON WITH DIALYTIKA + // U+0390: "ΐ" GREEK SMALL LETTER IOTA WITH DIALYTIKA AND TONOS + // U+03B0: "ΰ" GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND TONOS + // U+03AA: "Ϊ" GREEK CAPITAL LETTER IOTA WITH DIALYTIKA + // U+03AB: "Ϋ" GREEK CAPITAL LETTER UPSILON WITH DIALYTIKA + // U+0399: "Ι" GREEK CAPITAL LETTER IOTA + // U+03A5: "Υ" GREEK CAPITAL LETTER UPSILON + // U+0308: COMBINING DIAERESIS + // U+0301: COMBINING GRAVE ACCENT + assert_toTitleCaseOfKeyLabel(GREECE, + "\u03CA\u03CB\u0390\u03B0", + "\u03AA\u03AB\u0399\u0308\u0301\u03A5\u0308\u0301"); + } + + private static void assert_toTitleCaseOfKeyCode(final Locale locale, final int lowerCase, + final int expected) { + assertEquals(lowerCase + " in " + locale, expected, + StringUtils.toTitleCaseOfKeyCode(lowerCase, locale)); + } + + public void test_toTitleCaseOfKeyCode() { + assert_toTitleCaseOfKeyCode(US, Constants.CODE_ENTER, Constants.CODE_ENTER); + assert_toTitleCaseOfKeyCode(US, Constants.CODE_SPACE, Constants.CODE_SPACE); + assert_toTitleCaseOfKeyCode(US, Constants.CODE_COMMA, Constants.CODE_COMMA); + // U+0069: "i" LATIN SMALL LETTER I + // U+0131: "ı" LATIN SMALL LETTER DOTLESS I + // U+0130: "İ" LATIN SMALL LETTER I WITH DOT ABOVE + // U+0049: "I" LATIN SMALL LETTER I + assert_toTitleCaseOfKeyCode(US, 0x0069, 0x0049); // i -> I + assert_toTitleCaseOfKeyCode(US, 0x0131, 0x0049); // ı -> I + assert_toTitleCaseOfKeyCode(TURKEY, 0x0069, 0x0130); // i -> İ + assert_toTitleCaseOfKeyCode(TURKEY, 0x0131, 0x0049); // ı -> I + // U+00DF: "ß" LATIN SMALL LETTER SHARP S + // The title case of "ß" is "SS". + assert_toTitleCaseOfKeyCode(US, 0x00DF, Constants.CODE_UNSPECIFIED); + // U+03AC: "ά" GREEK SMALL LETTER ALPHA WITH TONOS + // U+0386: "Ά" GREEK CAPITAL LETTER ALPHA WITH TONOS + assert_toTitleCaseOfKeyCode(GREECE, 0x03AC, 0x0386); + // U+03CA: "ϊ" GREEK SMALL LETTER IOTA WITH DIALYTIKA + // U+03AA: "Ϊ" GREEK CAPITAL LETTER IOTA WITH DIALYTIKA + assert_toTitleCaseOfKeyCode(GREECE, 0x03CA, 0x03AA); + // U+03B0: "ΰ" GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND TONOS + // The title case of "ΰ" is "\u03A5\u0308\u0301". + assert_toTitleCaseOfKeyCode(GREECE, 0x03B0, Constants.CODE_UNSPECIFIED); + } + + private static void assert_capitalizeFirstCodePoint(final Locale locale, final String text, + final String expected) { + assertEquals(text + " in " + locale, expected, + StringUtils.capitalizeFirstCodePoint(text, locale)); + } + + public void test_capitalizeFirstCodePoint() { + assert_capitalizeFirstCodePoint(US, "", ""); + assert_capitalizeFirstCodePoint(US, "a", "A"); + assert_capitalizeFirstCodePoint(US, "à", "À"); + assert_capitalizeFirstCodePoint(US, "ß", "SS"); + assert_capitalizeFirstCodePoint(US, "text", "Text"); + assert_capitalizeFirstCodePoint(US, "iGoogle", "IGoogle"); + assert_capitalizeFirstCodePoint(TURKEY, "iyi", "İyi"); + assert_capitalizeFirstCodePoint(TURKEY, "ısırdı", "Isırdı"); + assert_capitalizeFirstCodePoint(GREECE, "ά", "Ά"); + assert_capitalizeFirstCodePoint(GREECE, "άνεση", "Άνεση"); + } + + private static void assert_capitalizeFirstAndDowncaseRest(final Locale locale, + final String text, final String expected) { + assertEquals(text + " in " + locale, expected, + StringUtils.capitalizeFirstAndDowncaseRest(text, locale)); + } + + public void test_capitalizeFirstAndDowncaseRest() { + assert_capitalizeFirstAndDowncaseRest(US, "", ""); + assert_capitalizeFirstAndDowncaseRest(US, "a", "A"); + assert_capitalizeFirstAndDowncaseRest(US, "à", "À"); + assert_capitalizeFirstAndDowncaseRest(US, "ß", "SS"); + assert_capitalizeFirstAndDowncaseRest(US, "text", "Text"); + assert_capitalizeFirstAndDowncaseRest(US, "iGoogle", "Igoogle"); + assert_capitalizeFirstAndDowncaseRest(US, "invite", "Invite"); + assert_capitalizeFirstAndDowncaseRest(US, "INVITE", "Invite"); + assert_capitalizeFirstAndDowncaseRest(TURKEY, "iyi", "İyi"); + assert_capitalizeFirstAndDowncaseRest(TURKEY, "İYİ", "İyi"); + assert_capitalizeFirstAndDowncaseRest(TURKEY, "ısırdı", "Isırdı"); + assert_capitalizeFirstAndDowncaseRest(TURKEY, "ISIRDI", "Isırdı"); + assert_capitalizeFirstAndDowncaseRest(GREECE, "ά", "Ά"); + assert_capitalizeFirstAndDowncaseRest(GREECE, "άνεση", "Άνεση"); + assert_capitalizeFirstAndDowncaseRest(GREECE, "ΆΝΕΣΗ", "Άνεση"); + } + public void testContainsInArray() { assertFalse("empty array", StringUtils.containsInArray("key", new String[0])); assertFalse("not in 1 element", StringUtils.containsInArray("key", new String[] { @@ -246,16 +401,6 @@ public class StringAndJsonUtilsTests extends AndroidTestCase { assertTrue(bytesStr.equals(bytesStr2)); } - public void testJsonUtils() { - final Object[] objs = new Object[] { 1, "aaa", "bbb", 3 }; - final List<Object> objArray = Arrays.asList(objs); - final String str = JsonUtils.listToJsonStr(objArray); - final List<Object> newObjArray = JsonUtils.jsonStrToList(str); - for (int i = 0; i < objs.length; ++i) { - assertEquals(objs[i], newObjArray.get(i)); - } - } - public void testToCodePointArray() { final String STR_WITH_SUPPLEMENTARY_CHAR = "abcde\uD861\uDED7fgh\u0000\u2002\u2003\u3000xx"; final int[] EXPECTED_RESULT = new int[] { 'a', 'b', 'c', 'd', 'e', 0x286D7, 'f', 'g', 'h', @@ -329,171 +474,4 @@ public class StringAndJsonUtilsTests extends AndroidTestCase { assertEquals(1, StringUtils.getTrailingSingleQuotesCount("'word'")); assertEquals(0, StringUtils.getTrailingSingleQuotesCount("I'm")); } - - private static void assertSpanCount(final int expectedCount, final CharSequence cs) { - final int actualCount; - if (cs instanceof Spanned) { - final Spanned spanned = (Spanned) cs; - actualCount = spanned.getSpans(0, spanned.length(), Object.class).length; - } else { - actualCount = 0; - } - assertEquals(expectedCount, actualCount); - } - - private static void assertSpan(final CharSequence cs, final Object expectedSpan, - final int expectedStart, final int expectedEnd, final int expectedFlags) { - assertTrue(cs instanceof Spanned); - final Spanned spanned = (Spanned) cs; - final Object[] actualSpans = spanned.getSpans(0, spanned.length(), Object.class); - for (Object actualSpan : actualSpans) { - if (actualSpan == expectedSpan) { - final int actualStart = spanned.getSpanStart(actualSpan); - final int actualEnd = spanned.getSpanEnd(actualSpan); - final int actualFlags = spanned.getSpanFlags(actualSpan); - assertEquals(expectedStart, actualStart); - assertEquals(expectedEnd, actualEnd); - assertEquals(expectedFlags, actualFlags); - return; - } - } - assertTrue(false); - } - - public void testSplitCharSequenceWithSpan() { - // text: " a bcd efg hij " - // span1: ^^^^^^^ - // span2: ^^^^^ - // span3: ^ - final SpannableString spannableString = new SpannableString(" a bcd efg hij "); - final Object span1 = new Object(); - final Object span2 = new Object(); - final Object span3 = new Object(); - final int SPAN1_FLAGS = Spanned.SPAN_EXCLUSIVE_EXCLUSIVE; - final int SPAN2_FLAGS = Spanned.SPAN_EXCLUSIVE_INCLUSIVE; - final int SPAN3_FLAGS = Spanned.SPAN_INCLUSIVE_INCLUSIVE; - spannableString.setSpan(span1, 0, 7, SPAN1_FLAGS); - spannableString.setSpan(span2, 0, 5, SPAN2_FLAGS); - spannableString.setSpan(span3, 12, 13, SPAN3_FLAGS); - final CharSequence[] charSequencesFromSpanned = StringUtils.split( - spannableString, " ", true /* preserveTrailingEmptySegmengs */); - final CharSequence[] charSequencesFromString = StringUtils.split( - spannableString.toString(), " ", true /* preserveTrailingEmptySegmengs */); - - - assertEquals(7, charSequencesFromString.length); - assertEquals(7, charSequencesFromSpanned.length); - - // text: "" - // span1: ^ - // span2: ^ - // span3: - assertEquals("", charSequencesFromString[0].toString()); - assertSpanCount(0, charSequencesFromString[0]); - assertEquals("", charSequencesFromSpanned[0].toString()); - assertSpanCount(2, charSequencesFromSpanned[0]); - assertSpan(charSequencesFromSpanned[0], span1, 0, 0, SPAN1_FLAGS); - assertSpan(charSequencesFromSpanned[0], span2, 0, 0, SPAN2_FLAGS); - - // text: "a" - // span1: ^ - // span2: ^ - // span3: - assertEquals("a", charSequencesFromString[1].toString()); - assertSpanCount(0, charSequencesFromString[1]); - assertEquals("a", charSequencesFromSpanned[1].toString()); - assertSpanCount(2, charSequencesFromSpanned[1]); - assertSpan(charSequencesFromSpanned[1], span1, 0, 1, SPAN1_FLAGS); - assertSpan(charSequencesFromSpanned[1], span2, 0, 1, SPAN2_FLAGS); - - // text: "bcd" - // span1: ^^^ - // span2: ^^ - // span3: - assertEquals("bcd", charSequencesFromString[2].toString()); - assertSpanCount(0, charSequencesFromString[2]); - assertEquals("bcd", charSequencesFromSpanned[2].toString()); - assertSpanCount(2, charSequencesFromSpanned[2]); - assertSpan(charSequencesFromSpanned[2], span1, 0, 3, SPAN1_FLAGS); - assertSpan(charSequencesFromSpanned[2], span2, 0, 2, SPAN2_FLAGS); - - // text: "efg" - // span1: - // span2: - // span3: - assertEquals("efg", charSequencesFromString[3].toString()); - assertSpanCount(0, charSequencesFromString[3]); - assertEquals("efg", charSequencesFromSpanned[3].toString()); - assertSpanCount(0, charSequencesFromSpanned[3]); - - // text: "hij" - // span1: - // span2: - // span3: ^ - assertEquals("hij", charSequencesFromString[4].toString()); - assertSpanCount(0, charSequencesFromString[4]); - assertEquals("hij", charSequencesFromSpanned[4].toString()); - assertSpanCount(1, charSequencesFromSpanned[4]); - assertSpan(charSequencesFromSpanned[4], span3, 1, 2, SPAN3_FLAGS); - - // text: "" - // span1: - // span2: - // span3: - assertEquals("", charSequencesFromString[5].toString()); - assertSpanCount(0, charSequencesFromString[5]); - assertEquals("", charSequencesFromSpanned[5].toString()); - assertSpanCount(0, charSequencesFromSpanned[5]); - - // text: "" - // span1: - // span2: - // span3: - assertEquals("", charSequencesFromString[6].toString()); - assertSpanCount(0, charSequencesFromString[6]); - assertEquals("", charSequencesFromSpanned[6].toString()); - assertSpanCount(0, charSequencesFromSpanned[6]); - } - - public void testSplitCharSequencePreserveTrailingEmptySegmengs() { - assertEquals(1, StringUtils.split("", " ", - false /* preserveTrailingEmptySegmengs */).length); - assertEquals(1, StringUtils.split(new SpannedString(""), " ", - false /* preserveTrailingEmptySegmengs */).length); - - assertEquals(1, StringUtils.split("", " ", - true /* preserveTrailingEmptySegmengs */).length); - assertEquals(1, StringUtils.split(new SpannedString(""), " ", - true /* preserveTrailingEmptySegmengs */).length); - - assertEquals(0, StringUtils.split(" ", " ", - false /* preserveTrailingEmptySegmengs */).length); - assertEquals(0, StringUtils.split(new SpannedString(" "), " ", - false /* preserveTrailingEmptySegmengs */).length); - - assertEquals(2, StringUtils.split(" ", " ", - true /* preserveTrailingEmptySegmengs */).length); - assertEquals(2, StringUtils.split(new SpannedString(" "), " ", - true /* preserveTrailingEmptySegmengs */).length); - - assertEquals(3, StringUtils.split("a b c ", " ", - false /* preserveTrailingEmptySegmengs */).length); - assertEquals(3, StringUtils.split(new SpannedString("a b c "), " ", - false /* preserveTrailingEmptySegmengs */).length); - - assertEquals(5, StringUtils.split("a b c ", " ", - true /* preserveTrailingEmptySegmengs */).length); - assertEquals(5, StringUtils.split(new SpannedString("a b c "), " ", - true /* preserveTrailingEmptySegmengs */).length); - - assertEquals(6, StringUtils.split("a b ", " ", - false /* preserveTrailingEmptySegmengs */).length); - assertEquals(6, StringUtils.split(new SpannedString("a b "), " ", - false /* preserveTrailingEmptySegmengs */).length); - - assertEquals(7, StringUtils.split("a b ", " ", - true /* preserveTrailingEmptySegmengs */).length); - assertEquals(7, StringUtils.split(new SpannedString("a b "), " ", - true /* preserveTrailingEmptySegmengs */).length); - } } diff --git a/tests/src/com/android/inputmethod/latin/common/UnicodeSurrogateTests.java b/tests/src/com/android/inputmethod/latin/common/UnicodeSurrogateTests.java new file mode 100644 index 000000000..59bb08292 --- /dev/null +++ b/tests/src/com/android/inputmethod/latin/common/UnicodeSurrogateTests.java @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2015 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.common; + +import android.test.AndroidTestCase; +import android.test.suitebuilder.annotation.SmallTest; + +@SmallTest +public class UnicodeSurrogateTests extends AndroidTestCase { + + public void testIsLowSurrogate() { + assertFalse(UnicodeSurrogate.isLowSurrogate('\uD7FF')); + assertTrue(UnicodeSurrogate.isLowSurrogate('\uD83D')); + assertFalse(UnicodeSurrogate.isLowSurrogate('\uDC00')); + } + + public void testIsHighSurrogate() { + assertFalse(UnicodeSurrogate.isHighSurrogate('\uDBFF')); + assertTrue(UnicodeSurrogate.isHighSurrogate('\uDE25')); + assertFalse(UnicodeSurrogate.isHighSurrogate('\uE000')); + } +} diff --git a/tests/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderEncoderTests.java b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderEncoderTests.java deleted file mode 100644 index 406046a74..000000000 --- a/tests/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderEncoderTests.java +++ /dev/null @@ -1,696 +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.suitebuilder.annotation.LargeTest; -import android.util.Log; -import android.util.Pair; -import android.util.SparseArray; - -import com.android.inputmethod.latin.BinaryDictionary; -import com.android.inputmethod.latin.makedict.BinaryDictDecoderUtils.CharEncoding; -import com.android.inputmethod.latin.makedict.BinaryDictDecoderUtils.DictBuffer; -import com.android.inputmethod.latin.makedict.FormatSpec.FormatOptions; -import com.android.inputmethod.latin.makedict.FusionDictionary.PtNode; -import com.android.inputmethod.latin.makedict.FusionDictionary.PtNodeArray; -import com.android.inputmethod.latin.utils.BinaryDictionaryUtils; -import com.android.inputmethod.latin.utils.ByteArrayDictBuffer; - -import java.io.File; -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.Locale; -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 = 300; - private static final int DEFAULT_CODE_POINT_SET_SIZE = 50; - private static final int LARGE_CODE_POINT_SET_SIZE = 300; - 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 ArrayList<String> sWords = new ArrayList<>(); - private static final ArrayList<String> sWordsWithVariousCodePoints = new ArrayList<>(); - private static final SparseArray<List<Integer>> sEmptyBigrams = new SparseArray<>(); - private static final SparseArray<List<Integer>> sStarBigrams = new SparseArray<>(); - private static final SparseArray<List<Integer>> sChainBigrams = new SparseArray<>(); - private static final HashMap<String, List<String>> sShortcuts = new HashMap<>(); - - public BinaryDictDecoderEncoderTests() { - this(System.currentTimeMillis(), DEFAULT_MAX_UNIGRAMS); - } - - public BinaryDictDecoderEncoderTests(final long seed, final int maxUnigrams) { - super(); - BinaryDictionaryUtils.setCurrentTimeForTest(0); - Log.e(TAG, "Testing dictionary: seed is " + seed); - final Random random = new Random(seed); - sWords.clear(); - sWordsWithVariousCodePoints.clear(); - generateWords(maxUnigrams, 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>()); - // MAX - 1 because we added one above already - final int maxBigrams = Math.min(sWords.size(), FormatSpec.MAX_BIGRAMS_IN_A_PTNODE - 1); - for (int i = 1; i < maxBigrams; ++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)); - } - } - } - - @Override - protected void setUp() throws Exception { - super.setUp(); - BinaryDictionaryUtils.setCurrentTimeForTest(0); - } - - @Override - protected void tearDown() throws Exception { - // Quit test mode. - BinaryDictionaryUtils.setCurrentTimeForTest(-1); - super.tearDown(); - } - - private void generateWords(final int number, final Random random) { - final int[] codePointSet = CodePointUtils.generateCodePointSet(DEFAULT_CODE_POINT_SET_SIZE, - random); - final Set<String> wordSet = new HashSet<>(); - while (wordSet.size() < number) { - wordSet.add(CodePointUtils.generateWord(random, codePointSet)); - } - sWords.addAll(wordSet); - - final int[] largeCodePointSet = CodePointUtils.generateCodePointSet( - LARGE_CODE_POINT_SET_SIZE, random); - wordSet.clear(); - while (wordSet.size() < number) { - wordSet.add(CodePointUtils.generateWord(random, largeCodePointSet)); - } - sWordsWithVariousCodePoints.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 = new ArrayList<>(); - if (shortcutMap != null && shortcutMap.containsKey(word)) { - for (final String shortcut : shortcutMap.get(word)) { - shortcuts.add(new WeightedString(shortcut, UNIGRAM_FREQ)); - } - } - dict.add(word, new ProbabilityInfo(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), new ProbabilityInfo(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 = BinaryDictUtils.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 == BinaryDictUtils.USE_BYTE_BUFFER) ? "byte buffer" : "byte array"); - return result + " : version = " + formatOptions.mVersion; - } - - // 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) { - long now, diff = -1; - - FusionDictionary dict = null; - try { - final DictDecoder dictDecoder = BinaryDictIOUtils.getDictDecoder(file, 0, file.length(), - bufferType); - now = System.currentTimeMillis(); - dict = dictDecoder.readDictionaryBinary(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 = BinaryDictUtils.getDictFile(dictName, dictVersion, formatOptions, - getContext().getCacheDir()); - - final FusionDictionary dict = new FusionDictionary(new PtNodeArray(), - BinaryDictUtils.makeDictionaryOptions(dictName, dictVersion, formatOptions)); - 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")); - 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")); - results.add(runReadAndWrite(sWordsWithVariousCodePoints, sEmptyBigrams, - null /* shortcuts */, bufferType, formatOptions, - "unigram with various code points")); - } - - // 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) { - 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 = new ArrayList<>(); - - runReadAndWriteTests(results, BinaryDictUtils.USE_BYTE_BUFFER, - BinaryDictUtils.VERSION2_OPTIONS); - runReadAndWriteTests(results, BinaryDictUtils.USE_BYTE_BUFFER, - BinaryDictUtils.VERSION4_OPTIONS_WITHOUT_TIMESTAMP); - runReadAndWriteTests(results, BinaryDictUtils.USE_BYTE_BUFFER, - BinaryDictUtils.VERSION4_OPTIONS_WITH_TIMESTAMP); - for (final String result : results) { - Log.d(TAG, result); - } - } - - public void testReadAndWriteWithByteArray() { - final List<String> results = new ArrayList<>(); - - runReadAndWriteTests(results, BinaryDictUtils.USE_BYTE_ARRAY, - BinaryDictUtils.VERSION2_OPTIONS); - runReadAndWriteTests(results, BinaryDictUtils.USE_BYTE_ARRAY, - BinaryDictUtils.VERSION4_OPTIONS_WITHOUT_TIMESTAMP); - runReadAndWriteTests(results, BinaryDictUtils.USE_BYTE_ARRAY, - BinaryDictUtils.VERSION4_OPTIONS_WITH_TIMESTAMP); - - 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, - final boolean checkProbability) { - // check unigrams - final Set<String> actualWordsSet = new HashSet<>(resultWords.values()); - final Set<String> expectedWordsSet = new HashSet<>(expectedWords); - assertEquals(actualWordsSet, expectedWordsSet); - if (checkProbability) { - for (int freq : resultFrequencies.values()) { - assertEquals(freq, UNIGRAM_FREQ); - } - } - - // check bigrams - final HashMap<String, Set<String>> expBigrams = new HashMap<>(); - 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 HashSet<String>()); - } - expBigrams.get(word1).add(expectedWords.get(w2)); - } - } - - final HashMap<String, Set<String>> actBigrams = new HashMap<>(); - 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 HashSet<String>()); - } - actBigrams.get(word1).add(word2); - - if (checkProbability) { - 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 boolean checkProbability) { - final TreeMap<Integer, String> resultWords = new TreeMap<>(); - final TreeMap<Integer, ArrayList<PendingAttribute>> resultBigrams = new TreeMap<>(); - final TreeMap<Integer, Integer> resultFreqs = new TreeMap<>(); - - long now = -1, diff = -1; - try { - final DictDecoder dictDecoder = BinaryDictIOUtils.getDictDecoder(file, 0, file.length(), - bufferType); - 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); - } - - checkWordMap(words, bigrams, resultWords, resultFreqs, resultBigrams, checkProbability); - 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 = BinaryDictUtils.getDictFile(dictName, dictVersion, formatOptions, - getContext().getCacheDir()); - - // making the dictionary from lists of words. - final FusionDictionary dict = new FusionDictionary(new PtNodeArray(), - BinaryDictUtils.makeDictionaryOptions(dictName, dictVersion, formatOptions)); - addUnigrams(words.size(), dict, words, null /* shortcutMap */); - addBigrams(dict, words, bigrams); - - timeWritingDictToFile(file, dict, formatOptions); - - // Caveat: Currently, the Java code to read a v4 dictionary doesn't calculate the - // probability when there's a timestamp for the entry. - // TODO: Abandon the Java code, and implement the v4 dictionary reading code in native. - long wordMap = timeAndCheckReadUnigramsAndBigramsBinary(file, words, bigrams, bufferType, - !formatOptions.mHasTimestamp /* checkProbability */); - long fullReading = timeReadingAndCheckDict(file, words, bigrams, null /* shortcutMap */, - bufferType); - - 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 = new ArrayList<>(); - - runReadUnigramsAndBigramsTests(results, BinaryDictUtils.USE_BYTE_BUFFER, - BinaryDictUtils.VERSION2_OPTIONS); - - for (final String result : results) { - Log.d(TAG, result); - } - } - - public void testReadUnigramsAndBigramsBinaryWithByteArray() { - final ArrayList<String> results = new ArrayList<>(); - - runReadUnigramsAndBigramsTests(results, BinaryDictUtils.USE_BYTE_ARRAY, - BinaryDictUtils.VERSION2_OPTIONS); - - 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); - - DictionaryHeader 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.mBodyOffset, - address).mWord; - } - - private long checkGetTerminalPosition(final DictDecoder dictDecoder, final String word, - final boolean contained) { - 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 = BinaryDictUtils.getDictFile(dictName, dictVersion, formatOptions, - getContext().getCacheDir()); - - final FusionDictionary dict = new FusionDictionary(new PtNodeArray(), - BinaryDictUtils.makeDictionaryOptions(dictName, dictVersion, formatOptions)); - addUnigrams(sWords.size(), dict, sWords, null /* shortcutMap */); - addBigrams(dict, words, bigrams); - timeWritingDictToFile(file, dict, formatOptions); - - final DictDecoder dictDecoder = BinaryDictIOUtils.getDictDecoder(file, 0, file.length(), - DictDecoder.USE_BYTEARRAY); - try { - dictDecoder.openDictBuffer(); - } catch (IOException e) { - Log.e(TAG, "IOException while opening the buffer", e); - } catch (UnsupportedFormatException e) { - 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), 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, false); - } - } - - private void runGetTerminalPositionTests(final int bufferType, - final FormatOptions formatOptions) { - runGetTerminalPosition(sWords, sEmptyBigrams, bufferType, formatOptions, "unigram"); - } - - public void testGetTerminalPosition() { - final ArrayList<String> results = new ArrayList<>(); - - runGetTerminalPositionTests(BinaryDictUtils.USE_BYTE_ARRAY, - BinaryDictUtils.VERSION2_OPTIONS); - runGetTerminalPositionTests(BinaryDictUtils.USE_BYTE_BUFFER, - BinaryDictUtils.VERSION2_OPTIONS); - - for (final String result : results) { - Log.d(TAG, result); - } - } - - public void testVer2DictGetWordProperty() { - final FormatOptions formatOptions = BinaryDictUtils.VERSION2_OPTIONS; - final ArrayList<String> words = sWords; - final HashMap<String, List<String>> shortcuts = sShortcuts; - final String dictName = "testGetWordProperty"; - final String dictVersion = Long.toString(System.currentTimeMillis()); - final FusionDictionary dict = new FusionDictionary(new PtNodeArray(), - BinaryDictUtils.makeDictionaryOptions(dictName, dictVersion, formatOptions)); - addUnigrams(words.size(), dict, words, shortcuts); - addBigrams(dict, words, sEmptyBigrams); - final File file = BinaryDictUtils.getDictFile(dictName, dictVersion, formatOptions, - getContext().getCacheDir()); - file.delete(); - timeWritingDictToFile(file, dict, formatOptions); - final BinaryDictionary binaryDictionary = new BinaryDictionary(file.getAbsolutePath(), - 0 /* offset */, file.length(), true /* useFullEditDistance */, - Locale.ENGLISH, dictName, false /* isUpdatable */); - for (final String word : words) { - final WordProperty wordProperty = binaryDictionary.getWordProperty(word, - false /* isBeginningOfSentence */); - assertEquals(word, wordProperty.mWord); - assertEquals(UNIGRAM_FREQ, wordProperty.getProbability()); - if (shortcuts.containsKey(word)) { - assertEquals(shortcuts.get(word).size(), wordProperty.mShortcutTargets.size()); - final List<String> shortcutList = shortcuts.get(word); - assertTrue(wordProperty.mHasShortcuts); - for (final WeightedString shortcutTarget : wordProperty.mShortcutTargets) { - assertTrue(shortcutList.contains(shortcutTarget.mWord)); - assertEquals(UNIGRAM_FREQ, shortcutTarget.getProbability()); - shortcutList.remove(shortcutTarget.mWord); - } - assertTrue(shortcutList.isEmpty()); - } - } - } - - public void testVer2DictIteration() { - final FormatOptions formatOptions = BinaryDictUtils.VERSION2_OPTIONS; - final ArrayList<String> words = sWords; - final HashMap<String, List<String>> shortcuts = sShortcuts; - final SparseArray<List<Integer>> bigrams = sEmptyBigrams; - final String dictName = "testGetWordProperty"; - final String dictVersion = Long.toString(System.currentTimeMillis()); - final FusionDictionary dict = new FusionDictionary(new PtNodeArray(), - BinaryDictUtils.makeDictionaryOptions(dictName, dictVersion, formatOptions)); - addUnigrams(words.size(), dict, words, shortcuts); - addBigrams(dict, words, bigrams); - final File file = BinaryDictUtils.getDictFile(dictName, dictVersion, formatOptions, - getContext().getCacheDir()); - timeWritingDictToFile(file, dict, formatOptions); - Log.d(TAG, file.getAbsolutePath()); - final BinaryDictionary binaryDictionary = new BinaryDictionary(file.getAbsolutePath(), - 0 /* offset */, file.length(), true /* useFullEditDistance */, - Locale.ENGLISH, dictName, false /* isUpdatable */); - - final HashSet<String> wordSet = new HashSet<>(words); - final HashSet<Pair<String, String>> bigramSet = new HashSet<>(); - - for (int i = 0; i < words.size(); i++) { - final List<Integer> bigramList = bigrams.get(i); - if (bigramList != null) { - for (final Integer word1Index : bigramList) { - final String word1 = words.get(word1Index); - bigramSet.add(new Pair<>(words.get(i), word1)); - } - } - } - int token = 0; - do { - final BinaryDictionary.GetNextWordPropertyResult result = - binaryDictionary.getNextWordProperty(token); - final WordProperty wordProperty = result.mWordProperty; - final String word0 = wordProperty.mWord; - assertEquals(UNIGRAM_FREQ, wordProperty.mProbabilityInfo.mProbability); - wordSet.remove(word0); - if (shortcuts.containsKey(word0)) { - assertEquals(shortcuts.get(word0).size(), wordProperty.mShortcutTargets.size()); - final List<String> shortcutList = shortcuts.get(word0); - assertNotNull(wordProperty.mShortcutTargets); - for (final WeightedString shortcutTarget : wordProperty.mShortcutTargets) { - assertTrue(shortcutList.contains(shortcutTarget.mWord)); - assertEquals(UNIGRAM_FREQ, shortcutTarget.getProbability()); - shortcutList.remove(shortcutTarget.mWord); - } - assertTrue(shortcutList.isEmpty()); - } - for (int j = 0; j < wordProperty.mBigrams.size(); j++) { - final String word1 = wordProperty.mBigrams.get(j).mWord; - final Pair<String, String> bigram = new Pair<>(word0, word1); - assertTrue(bigramSet.contains(bigram)); - bigramSet.remove(bigram); - } - token = result.mNextToken; - } while (token != 0); - assertTrue(wordSet.isEmpty()); - assertTrue(bigramSet.isEmpty()); - } -} diff --git a/tests/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderUtils.java b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderUtils.java index 96604a197..be75565bb 100644 --- a/tests/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderUtils.java +++ b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderUtils.java @@ -17,11 +17,16 @@ package com.android.inputmethod.latin.makedict; import com.android.inputmethod.annotations.UsedForTesting; +import com.android.inputmethod.latin.makedict.UnsupportedFormatException; import java.io.File; import java.io.IOException; import java.io.OutputStream; import java.nio.ByteBuffer; +import java.util.HashMap; +import java.util.LinkedList; + +import javax.annotation.Nonnull; /** * Decodes binary files for a FusionDictionary. @@ -109,15 +114,20 @@ public final class BinaryDictDecoderUtils { * A class grouping utility function for our specific character encoding. */ static final class CharEncoding { - private static final int MINIMAL_ONE_BYTE_CHARACTER_VALUE = 0x20; - private static final int MAXIMAL_ONE_BYTE_CHARACTER_VALUE = 0xFF; /** * Helper method to find out whether this code fits on one byte */ - private static boolean fitsOnOneByte(final int character) { - return character >= MINIMAL_ONE_BYTE_CHARACTER_VALUE - && character <= MAXIMAL_ONE_BYTE_CHARACTER_VALUE; + private static boolean fitsOnOneByte(final int character, + final HashMap<Integer, Integer> codePointToOneByteCodeMap) { + int codePoint = character; + if (codePointToOneByteCodeMap != null) { + if (codePointToOneByteCodeMap.containsKey(character)) { + codePoint = codePointToOneByteCodeMap.get(character); + } + } + return codePoint >= FormatSpec.MINIMAL_ONE_BYTE_CHARACTER_VALUE + && codePoint <= FormatSpec.MAXIMAL_ONE_BYTE_CHARACTER_VALUE; } /** @@ -137,9 +147,10 @@ public final class BinaryDictDecoderUtils { * @param character the character code. * @return the size in binary encoded-form, either 1 or 3 bytes. */ - static int getCharSize(final int character) { + static int getCharSize(final int character, + final HashMap<Integer, Integer> codePointToOneByteCodeMap) { // See char encoding in FusionDictionary.java - if (fitsOnOneByte(character)) return 1; + if (fitsOnOneByte(character, codePointToOneByteCodeMap)) return 1; if (FormatSpec.INVALID_CHARACTER == character) return 1; return 3; } @@ -147,9 +158,10 @@ public final class BinaryDictDecoderUtils { /** * Compute the byte size of a character array. */ - static int getCharArraySize(final int[] chars) { + static int getCharArraySize(final int[] chars, + final HashMap<Integer, Integer> codePointToOneByteCodeMap) { int size = 0; - for (int character : chars) size += getCharSize(character); + for (int character : chars) size += getCharSize(character, codePointToOneByteCodeMap); return size; } @@ -158,12 +170,21 @@ public final class BinaryDictDecoderUtils { * * @param codePoints the code point array to write. * @param buffer the byte buffer to write to. - * @param index the index in buffer to write the character array to. + * @param fromIndex the index in buffer to write the character array to. + * @param codePointToOneByteCodeMap the map to convert the code point. * @return the index after the last character. */ - static int writeCharArray(final int[] codePoints, final byte[] buffer, int index) { + static int writeCharArray(final int[] codePoints, final byte[] buffer, final int fromIndex, + final HashMap<Integer, Integer> codePointToOneByteCodeMap) { + int index = fromIndex; for (int codePoint : codePoints) { - if (1 == getCharSize(codePoint)) { + if (codePointToOneByteCodeMap != null) { + if (codePointToOneByteCodeMap.containsKey(codePoint)) { + // Convert code points + codePoint = codePointToOneByteCodeMap.get(codePoint); + } + } + if (1 == getCharSize(codePoint, codePointToOneByteCodeMap)) { buffer[index++] = (byte)codePoint; } else { buffer[index++] = (byte)(0xFF & (codePoint >> 16)); @@ -184,12 +205,19 @@ public final class BinaryDictDecoderUtils { * @param word the string to write. * @return the size written, in bytes. */ - static int writeString(final byte[] buffer, final int origin, final String word) { + static int writeString(final byte[] buffer, final int origin, final String word, + final HashMap<Integer, Integer> codePointToOneByteCodeMap) { final int length = word.length(); int index = origin; for (int i = 0; i < length; i = word.offsetByCodePoints(i, 1)) { - final int codePoint = word.codePointAt(i); - if (1 == getCharSize(codePoint)) { + int codePoint = word.codePointAt(i); + if (codePointToOneByteCodeMap != null) { + if (codePointToOneByteCodeMap.containsKey(codePoint)) { + // Convert code points + codePoint = codePointToOneByteCodeMap.get(codePoint); + } + } + if (1 == getCharSize(codePoint, codePointToOneByteCodeMap)) { buffer[index++] = (byte)codePoint; } else { buffer[index++] = (byte)(0xFF & (codePoint >> 16)); @@ -210,12 +238,13 @@ public final class BinaryDictDecoderUtils { * @param word the string to write. * @return the size written, in bytes. */ - static int writeString(final OutputStream stream, final String word) throws IOException { + static int writeString(final OutputStream stream, final String word, + final HashMap<Integer, Integer> codePointToOneByteCodeMap) throws IOException { final int length = word.length(); int written = 0; for (int i = 0; i < length; i = word.offsetByCodePoints(i, 1)) { final int codePoint = word.codePointAt(i); - final int charSize = getCharSize(codePoint); + final int charSize = getCharSize(codePoint, codePointToOneByteCodeMap); if (1 == charSize) { stream.write((byte) codePoint); } else { @@ -253,7 +282,7 @@ public final class BinaryDictDecoderUtils { */ static int readChar(final DictBuffer dictBuffer) { int character = dictBuffer.readUnsignedByte(); - if (!fitsOnOneByte(character)) { + if (!fitsOnOneByte(character, null)) { if (FormatSpec.PTNODE_CHARACTERS_TERMINATOR == character) { return FormatSpec.INVALID_CHARACTER; } @@ -271,10 +300,9 @@ public final class BinaryDictDecoderUtils { final int msb = dictBuffer.readUnsignedByte(); if (FormatSpec.MAX_PTNODES_FOR_ONE_BYTE_PTNODE_COUNT >= msb) { return msb; - } else { - return ((FormatSpec.MAX_PTNODES_FOR_ONE_BYTE_PTNODE_COUNT & msb) << 8) - + dictBuffer.readUnsignedByte(); } + return ((FormatSpec.MAX_PTNODES_FOR_ONE_BYTE_PTNODE_COUNT & msb) << 8) + + dictBuffer.readUnsignedByte(); } /** @@ -338,6 +366,43 @@ public final class BinaryDictDecoderUtils { } /** + * Helper method that brutally decodes a header from a byte array. + * + * @param headerBuffer a buffer containing the bytes of the header. + * @return a hashmap of the attributes stored in the header + */ + @Nonnull + public static HashMap<String, String> decodeHeaderAttributes(@Nonnull final byte[] headerBuffer) + throws UnsupportedFormatException { + final StringBuilder sb = new StringBuilder(); + final LinkedList<String> keyValues = new LinkedList<>(); + int index = 0; + while (index < headerBuffer.length) { + if (headerBuffer[index] == FormatSpec.PTNODE_CHARACTERS_TERMINATOR) { + keyValues.add(sb.toString()); + sb.setLength(0); + } else if (CharEncoding.fitsOnOneByte(headerBuffer[index] & 0xFF, + null /* codePointTable */)) { + sb.appendCodePoint(headerBuffer[index] & 0xFF); + } else { + sb.appendCodePoint(((headerBuffer[index] & 0xFF) << 16) + + ((headerBuffer[index + 1] & 0xFF) << 8) + + (headerBuffer[index + 2] & 0xFF)); + index += 2; + } + index += 1; + } + if ((keyValues.size() & 1) != 0) { + throw new UnsupportedFormatException("Odd number of attributes"); + } + final HashMap<String, String> attributes = new HashMap<>(); + for (int i = 0; i < keyValues.size(); i += 2) { + attributes.put(keyValues.get(i), keyValues.get(i + 1)); + } + return attributes; + } + + /** * Helper method to pass a file name instead of a File object to isBinaryDictionary. */ public static boolean isBinaryDictionary(final String filename) { diff --git a/tests/src/com/android/inputmethod/latin/makedict/BinaryDictEncoderUtils.java b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictEncoderUtils.java index eabd8d722..bd5136583 100644 --- a/tests/src/com/android/inputmethod/latin/makedict/BinaryDictEncoderUtils.java +++ b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictEncoderUtils.java @@ -16,9 +16,7 @@ package com.android.inputmethod.latin.makedict; -import com.android.inputmethod.annotations.UsedForTesting; import com.android.inputmethod.latin.makedict.BinaryDictDecoderUtils.CharEncoding; -import com.android.inputmethod.latin.makedict.BinaryDictDecoderUtils.DictBuffer; import com.android.inputmethod.latin.makedict.FormatSpec.FormatOptions; import com.android.inputmethod.latin.makedict.FusionDictionary.PtNode; import com.android.inputmethod.latin.makedict.FusionDictionary.PtNodeArray; @@ -27,6 +25,8 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.OutputStream; import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map.Entry; /** * Encodes binary files for a FusionDictionary. @@ -59,8 +59,9 @@ public class BinaryDictEncoderUtils { * @param characters the character array * @return the size of the char array, including the terminator if any */ - static int getPtNodeCharactersSize(final int[] characters) { - int size = CharEncoding.getCharArraySize(characters); + static int getPtNodeCharactersSize(final int[] characters, + final HashMap<Integer, Integer> codePointToOneByteCodeMap) { + int size = CharEncoding.getCharArraySize(characters, codePointToOneByteCodeMap); if (characters.length > 1) size += FormatSpec.PTNODE_TERMINATOR_SIZE; return size; } @@ -74,8 +75,9 @@ public class BinaryDictEncoderUtils { * @param ptNode the PtNode * @return the size of the char array, including the terminator if any */ - private static int getPtNodeCharactersSize(final PtNode ptNode) { - return getPtNodeCharactersSize(ptNode.mChars); + private static int getPtNodeCharactersSize(final PtNode ptNode, + final HashMap<Integer, Integer> codePointToOneByteCodeMap) { + return getPtNodeCharactersSize(ptNode.mChars, codePointToOneByteCodeMap); } /** @@ -88,49 +90,19 @@ public class BinaryDictEncoderUtils { } /** - * Compute the size of a shortcut in bytes. - */ - private static int getShortcutSize(final WeightedString shortcut) { - int size = FormatSpec.PTNODE_ATTRIBUTE_FLAGS_SIZE; - final String word = shortcut.mWord; - final int length = word.length(); - for (int i = 0; i < length; i = word.offsetByCodePoints(i, 1)) { - final int codePoint = word.codePointAt(i); - size += CharEncoding.getCharSize(codePoint); - } - size += FormatSpec.PTNODE_TERMINATOR_SIZE; - return size; - } - - /** - * Compute the size of a shortcut list in bytes. - * - * This is known in advance and does not change according to position in the file - * like address lists do. - */ - static int getShortcutListSize(final ArrayList<WeightedString> shortcutList) { - if (null == shortcutList || shortcutList.isEmpty()) return 0; - int size = FormatSpec.PTNODE_SHORTCUT_LIST_SIZE_SIZE; - for (final WeightedString shortcut : shortcutList) { - size += getShortcutSize(shortcut); - } - return size; - } - - /** * Compute the maximum size of a PtNode, assuming 3-byte addresses for everything. * * @param ptNode the PtNode to compute the size of. * @return the maximum size of the PtNode. */ - private static int getPtNodeMaximumSize(final PtNode ptNode) { - int size = getNodeHeaderSize(ptNode); + private static int getPtNodeMaximumSize(final PtNode ptNode, + final HashMap<Integer, Integer> codePointToOneByteCodeMap) { + int size = getNodeHeaderSize(ptNode, codePointToOneByteCodeMap); if (ptNode.isTerminal()) { // If terminal, one byte for the frequency. size += FormatSpec.PTNODE_FREQUENCY_SIZE; } size += FormatSpec.PTNODE_MAX_ADDRESS_SIZE; // For children address - size += getShortcutListSize(ptNode.mShortcutTargets); if (null != ptNode.mBigrams) { size += (FormatSpec.PTNODE_ATTRIBUTE_FLAGS_SIZE + FormatSpec.PTNODE_ATTRIBUTE_MAX_ADDRESS_SIZE) @@ -146,10 +118,11 @@ public class BinaryDictEncoderUtils { * * @param ptNodeArray the node array to compute the maximum size of. */ - private static void calculatePtNodeArrayMaximumSize(final PtNodeArray ptNodeArray) { + private static void calculatePtNodeArrayMaximumSize(final PtNodeArray ptNodeArray, + final HashMap<Integer, Integer> codePointToOneByteCodeMap) { int size = getPtNodeCountSize(ptNodeArray); for (PtNode node : ptNodeArray.mData) { - final int nodeSize = getPtNodeMaximumSize(node); + final int nodeSize = getPtNodeMaximumSize(node, codePointToOneByteCodeMap); node.mCachedSize = nodeSize; size += nodeSize; } @@ -161,8 +134,10 @@ public class BinaryDictEncoderUtils { * * @param ptNode the PtNode of which to compute the size of the header */ - private static int getNodeHeaderSize(final PtNode ptNode) { - return FormatSpec.PTNODE_FLAGS_SIZE + getPtNodeCharactersSize(ptNode); + private static int getNodeHeaderSize(final PtNode ptNode, + final HashMap<Integer, Integer> codePointToOneByteCodeMap) { + return FormatSpec.PTNODE_FLAGS_SIZE + getPtNodeCharactersSize(ptNode, + codePointToOneByteCodeMap); } /** @@ -188,8 +163,9 @@ public class BinaryDictEncoderUtils { } } - static int writeUIntToBuffer(final byte[] buffer, int position, final int value, + static int writeUIntToBuffer(final byte[] buffer, final int fromPosition, final int value, final int size) { + int position = fromPosition; switch(size) { case 4: buffer[position++] = (byte) ((value >> 24) & 0xFF); @@ -229,27 +205,6 @@ public class BinaryDictEncoderUtils { } } - @UsedForTesting - static void writeUIntToDictBuffer(final DictBuffer dictBuffer, final int value, - final int size) { - switch(size) { - case 4: - dictBuffer.put((byte) ((value >> 24) & 0xFF)); - /* fall through */ - case 3: - dictBuffer.put((byte) ((value >> 16) & 0xFF)); - /* fall through */ - case 2: - dictBuffer.put((byte) ((value >> 8) & 0xFF)); - /* fall through */ - case 1: - dictBuffer.put((byte) (value & 0xFF)); - break; - default: - /* nop */ - } - } - // End utility methods // This method is responsible for finding a nice ordering of the nodes that favors run-time @@ -313,11 +268,9 @@ public class BinaryDictEncoderUtils { return targetNodeArray.mCachedAddressAfterUpdate - (currentNodeArray.mCachedAddressAfterUpdate + offsetFromStartOfCurrentNodeArray); - } else { - return targetNodeArray.mCachedAddressBeforeUpdate - - (currentNodeArray.mCachedAddressBeforeUpdate - + offsetFromStartOfCurrentNodeArray); } + return targetNodeArray.mCachedAddressBeforeUpdate + - (currentNodeArray.mCachedAddressBeforeUpdate + offsetFromStartOfCurrentNodeArray); } /** @@ -345,9 +298,8 @@ public class BinaryDictEncoderUtils { final int newOffsetBasePoint = currentNodeArray.mCachedAddressAfterUpdate + offsetFromStartOfCurrentNodeArray; return targetPtNode.mCachedAddressAfterUpdate - newOffsetBasePoint; - } else { - return targetPtNode.mCachedAddressBeforeUpdate - oldOffsetBasePoint; } + return targetPtNode.mCachedAddressBeforeUpdate - oldOffsetBasePoint; } /** @@ -365,7 +317,8 @@ public class BinaryDictEncoderUtils { * @return false if none of the cached addresses inside the node array changed, true otherwise. */ private static boolean computeActualPtNodeArraySize(final PtNodeArray ptNodeArray, - final FusionDictionary dict) { + final FusionDictionary dict, + final HashMap<Integer, Integer> codePointToOneByteCodeMap) { boolean changed = false; int size = getPtNodeCountSize(ptNodeArray); for (PtNode ptNode : ptNodeArray.mData) { @@ -373,7 +326,7 @@ public class BinaryDictEncoderUtils { if (ptNode.mCachedAddressAfterUpdate != ptNode.mCachedAddressBeforeUpdate) { changed = true; } - int nodeSize = getNodeHeaderSize(ptNode); + int nodeSize = getNodeHeaderSize(ptNode, codePointToOneByteCodeMap); if (ptNode.isTerminal()) { nodeSize += FormatSpec.PTNODE_FREQUENCY_SIZE; } @@ -381,7 +334,6 @@ public class BinaryDictEncoderUtils { nodeSize += getByteSize(getOffsetToTargetNodeArrayDuringUpdate(ptNodeArray, nodeSize + size, ptNode.mChildren)); } - nodeSize += getShortcutListSize(ptNode.mShortcutTargets); if (null != ptNode.mBigrams) { for (WeightedString bigram : ptNode.mBigrams) { final int offset = getOffsetToTargetPtNodeDuringUpdate(ptNodeArray, @@ -452,10 +404,11 @@ public class BinaryDictEncoderUtils { * @return the same array it was passed. The nodes have been updated for address and size. */ /* package */ static ArrayList<PtNodeArray> computeAddresses(final FusionDictionary dict, - final ArrayList<PtNodeArray> flatNodes) { + final ArrayList<PtNodeArray> flatNodes, + final HashMap<Integer, Integer> codePointToOneByteCodeMap) { // First get the worst possible sizes and offsets for (final PtNodeArray n : flatNodes) { - calculatePtNodeArrayMaximumSize(n); + calculatePtNodeArrayMaximumSize(n, codePointToOneByteCodeMap); } final int offset = initializePtNodeArraysCachedAddresses(flatNodes); @@ -470,7 +423,8 @@ public class BinaryDictEncoderUtils { for (final PtNodeArray ptNodeArray : flatNodes) { ptNodeArray.mCachedAddressAfterUpdate = ptNodeArrayStartOffset; final int oldNodeArraySize = ptNodeArray.mCachedSize; - final boolean changed = computeActualPtNodeArraySize(ptNodeArray, dict); + final boolean changed = computeActualPtNodeArraySize(ptNodeArray, dict, + codePointToOneByteCodeMap); final int newNodeArraySize = ptNodeArray.mCachedSize; if (oldNodeArraySize < newNodeArraySize) { throw new RuntimeException("Increased size ?!"); @@ -521,12 +475,13 @@ public class BinaryDictEncoderUtils { * Helper method to write a children position to a file. * * @param buffer the buffer to write to. - * @param index the index in the buffer to write the address to. + * @param fromIndex the index in the buffer to write the address to. * @param position the position to write. * @return the size in bytes the address actually took. */ - /* package */ static int writeChildrenPosition(final byte[] buffer, int index, + /* package */ static int writeChildrenPosition(final byte[] buffer, final int fromIndex, final int position) { + int index = fromIndex; switch (getByteSize(position)) { case 1: buffer[index++] = (byte)position; @@ -548,42 +503,19 @@ public class BinaryDictEncoderUtils { } /** - * Helper method to write a signed children position to a file. - * - * @param buffer the buffer to write to. - * @param index the index in the buffer to write the address to. - * @param position the position to write. - * @return the size in bytes the address actually took. - */ - /* package */ static int writeSignedChildrenPosition(final byte[] buffer, int index, - final int position) { - if (!BinaryDictIOUtils.hasChildrenAddress(position)) { - buffer[index] = buffer[index + 1] = buffer[index + 2] = 0; - } else { - final int absPosition = Math.abs(position); - buffer[index++] = - (byte)((position < 0 ? FormatSpec.MSB8 : 0) | (0xFF & (absPosition >> 16))); - buffer[index++] = (byte)(0xFF & (absPosition >> 8)); - buffer[index++] = (byte)(0xFF & absPosition); - } - return 3; - } - - /** * Makes the flag value for a PtNode. * * @param hasMultipleChars whether the PtNode has multiple chars. * @param isTerminal whether the PtNode is terminal. * @param childrenAddressSize the size of a children address. - * @param hasShortcuts whether the PtNode has shortcuts. * @param hasBigrams whether the PtNode has bigrams. * @param isNotAWord whether the PtNode is not a word. - * @param isBlackListEntry whether the PtNode is a blacklist entry. + * @param isPossiblyOffensive whether the PtNode is a possibly offensive entry. * @return the flags */ static int makePtNodeFlags(final boolean hasMultipleChars, final boolean isTerminal, - final int childrenAddressSize, final boolean hasShortcuts, final boolean hasBigrams, - final boolean isNotAWord, final boolean isBlackListEntry) { + final int childrenAddressSize, final boolean hasBigrams, + final boolean isNotAWord, final boolean isPossiblyOffensive) { byte flags = 0; if (hasMultipleChars) flags |= FormatSpec.FLAG_HAS_MULTIPLE_CHARS; if (isTerminal) flags |= FormatSpec.FLAG_IS_TERMINAL; @@ -603,19 +535,17 @@ public class BinaryDictEncoderUtils { default: throw new RuntimeException("Node with a strange address"); } - if (hasShortcuts) flags |= FormatSpec.FLAG_HAS_SHORTCUT_TARGETS; if (hasBigrams) flags |= FormatSpec.FLAG_HAS_BIGRAMS; if (isNotAWord) flags |= FormatSpec.FLAG_IS_NOT_A_WORD; - if (isBlackListEntry) flags |= FormatSpec.FLAG_IS_BLACKLISTED; + if (isPossiblyOffensive) flags |= FormatSpec.FLAG_IS_POSSIBLY_OFFENSIVE; return flags; } /* package */ static byte makePtNodeFlags(final PtNode node, final int childrenOffset) { return (byte) makePtNodeFlags(node.mChars.length > 1, node.isTerminal(), getByteSize(childrenOffset), - node.mShortcutTargets != null && !node.mShortcutTargets.isEmpty(), node.mBigrams != null && !node.mBigrams.isEmpty(), - node.mIsNotAWord, node.mIsBlacklistEntry); + node.mIsNotAWord, node.mIsPossiblyOffensive); } /** @@ -628,8 +558,8 @@ public class BinaryDictEncoderUtils { * @param word the second bigram, for debugging purposes * @return the flags */ - /* package */ static final int makeBigramFlags(final boolean more, final int offset, - int bigramFrequency, final int unigramFrequency, final String word) { + /* package */ static int makeBigramFlags(final boolean more, final int offset, + final int bigramFrequency, final int unigramFrequency, final String word) { int bigramFlags = (more ? FormatSpec.FLAG_BIGRAM_SHORTCUT_ATTR_HAS_NEXT : 0) + (offset < 0 ? FormatSpec.FLAG_BIGRAM_ATTR_OFFSET_NEGATIVE : 0); switch (getByteSize(offset)) { @@ -645,13 +575,16 @@ public class BinaryDictEncoderUtils { default: throw new RuntimeException("Strange offset size"); } + final int frequency; if (unigramFrequency > bigramFrequency) { MakedictLog.e("Unigram freq is superior to bigram freq for \"" + word + "\". Bigram freq is " + bigramFrequency + ", unigram freq for " + word + " is " + unigramFrequency); - bigramFrequency = unigramFrequency; + frequency = unigramFrequency; + } else { + frequency = bigramFrequency; } - bigramFlags += getBigramFrequencyDiff(unigramFrequency, bigramFrequency) + bigramFlags += getBigramFrequencyDiff(unigramFrequency, frequency) & FormatSpec.FLAG_BIGRAM_SHORTCUT_ATTR_FREQUENCY; return bigramFlags; } @@ -694,21 +627,10 @@ public class BinaryDictEncoderUtils { return discretizedFrequency > 0 ? discretizedFrequency : 0; } - /** - * Makes the flag value for a shortcut. - * - * @param more whether there are more attributes after this one. - * @param frequency the frequency of the attribute, 0..15 - * @return the flags - */ - static final int makeShortcutFlags(final boolean more, final int frequency) { - return (more ? FormatSpec.FLAG_BIGRAM_SHORTCUT_ATTR_HAS_NEXT : 0) - + (frequency & FormatSpec.FLAG_BIGRAM_SHORTCUT_ATTR_FREQUENCY); - } - - /* package */ static final int getChildrenPosition(final PtNode ptNode) { + /* package */ static int getChildrenPosition(final PtNode ptNode, + final HashMap<Integer, Integer> codePointToOneByteCodeMap) { int positionOfChildrenPosField = ptNode.mCachedAddressAfterUpdate - + getNodeHeaderSize(ptNode); + + getNodeHeaderSize(ptNode, codePointToOneByteCodeMap); if (ptNode.isTerminal()) { // A terminal node has the frequency. // If positionOfChildrenPosField is incorrect, we may crash when jumping to the children @@ -725,19 +647,16 @@ public class BinaryDictEncoderUtils { * @param dict the dictionary the node array is a part of (for relative offsets). * @param dictEncoder the dictionary encoder. * @param ptNodeArray the node array to write. + * @param codePointToOneByteCodeMap the map to convert the code points. */ - @SuppressWarnings("unused") /* package */ static void writePlacedPtNodeArray(final FusionDictionary dict, - final DictEncoder dictEncoder, final PtNodeArray ptNodeArray) { + final DictEncoder dictEncoder, final PtNodeArray ptNodeArray, + final HashMap<Integer, Integer> codePointToOneByteCodeMap) { // TODO: Make the code in common with BinaryDictIOUtils#writePtNode dictEncoder.setPosition(ptNodeArray.mCachedAddressAfterUpdate); final int ptNodeCount = ptNodeArray.mData.size(); dictEncoder.writePtNodeCount(ptNodeCount); - final int parentPosition = - (ptNodeArray.mCachedParentAddress == FormatSpec.NO_PARENT_ADDRESS) - ? FormatSpec.NO_PARENT_ADDRESS - : ptNodeArray.mCachedParentAddress + ptNodeArray.mCachedAddressAfterUpdate; for (int i = 0; i < ptNodeCount; ++i) { final PtNode ptNode = ptNodeArray.mData.get(i); if (dictEncoder.getPosition() != ptNode.mCachedAddressAfterUpdate) { @@ -751,7 +670,7 @@ public class BinaryDictEncoderUtils { + FormatSpec.MAX_TERMINAL_FREQUENCY + " : " + ptNode.mProbabilityInfo.toString()); } - dictEncoder.writePtNode(ptNode, dict); + dictEncoder.writePtNode(ptNode, dict, codePointToOneByteCodeMap); } if (dictEncoder.getPosition() != ptNodeArray.mCachedAddressAfterUpdate + ptNodeArray.mCachedSize) { @@ -817,18 +736,26 @@ public class BinaryDictEncoderUtils { * @param destination the stream to write the file header to. * @param dict the dictionary to write. * @param formatOptions file format options. + * @param codePointOccurrenceArray code points ordered by occurrence count. * @return the size of the header. */ /* package */ static int writeDictionaryHeader(final OutputStream destination, - final FusionDictionary dict, final FormatOptions formatOptions) + final FusionDictionary dict, final FormatOptions formatOptions, + final ArrayList<Entry<Integer, Integer>> codePointOccurrenceArray) throws IOException, UnsupportedFormatException { final int version = formatOptions.mVersion; - if (version < FormatSpec.MINIMUM_SUPPORTED_VERSION - || version > FormatSpec.MAXIMUM_SUPPORTED_VERSION) { + if ((version >= FormatSpec.MINIMUM_SUPPORTED_STATIC_VERSION && + version <= FormatSpec.MAXIMUM_SUPPORTED_STATIC_VERSION) || ( + version >= FormatSpec.MINIMUM_SUPPORTED_DYNAMIC_VERSION && + version <= FormatSpec.MAXIMUM_SUPPORTED_DYNAMIC_VERSION)) { + // Dictionary is valid + } else { throw new UnsupportedFormatException("Requested file format version " + version - + ", but this implementation only supports versions " - + FormatSpec.MINIMUM_SUPPORTED_VERSION + " through " - + FormatSpec.MAXIMUM_SUPPORTED_VERSION); + + ", but this implementation only supports static versions " + + FormatSpec.MINIMUM_SUPPORTED_STATIC_VERSION + " through " + + FormatSpec.MAXIMUM_SUPPORTED_STATIC_VERSION + " and dynamic versions " + + FormatSpec.MINIMUM_SUPPORTED_DYNAMIC_VERSION + " through " + + FormatSpec.MAXIMUM_SUPPORTED_DYNAMIC_VERSION); } ByteArrayOutputStream headerBuffer = new ByteArrayOutputStream(256); @@ -856,8 +783,15 @@ public class BinaryDictEncoderUtils { // Write out the options. for (final String key : dict.mOptions.mAttributes.keySet()) { final String value = dict.mOptions.mAttributes.get(key); - CharEncoding.writeString(headerBuffer, key); - CharEncoding.writeString(headerBuffer, value); + CharEncoding.writeString(headerBuffer, key, null); + CharEncoding.writeString(headerBuffer, value, null); + } + // Write out the codePointTable if there is codePointOccurrenceArray. + if (codePointOccurrenceArray != null) { + final String codePointTableString = + encodeCodePointTable(codePointOccurrenceArray); + CharEncoding.writeString(headerBuffer, DictionaryHeader.CODE_POINT_TABLE_KEY, null); + CharEncoding.writeString(headerBuffer, codePointTableString, null); } final int size = headerBuffer.size(); final byte[] bytes = headerBuffer.toByteArray(); @@ -871,4 +805,35 @@ public class BinaryDictEncoderUtils { headerBuffer.close(); return size; } + + static final class CodePointTable { + final HashMap<Integer, Integer> mCodePointToOneByteCodeMap; + final ArrayList<Entry<Integer, Integer>> mCodePointOccurrenceArray; + + // Let code point table empty for version 200 dictionary which used in test + CodePointTable() { + mCodePointToOneByteCodeMap = null; + mCodePointOccurrenceArray = null; + } + + CodePointTable(final HashMap<Integer, Integer> codePointToOneByteCodeMap, + final ArrayList<Entry<Integer, Integer>> codePointOccurrenceArray) { + mCodePointToOneByteCodeMap = codePointToOneByteCodeMap; + mCodePointOccurrenceArray = codePointOccurrenceArray; + } + } + + private static String encodeCodePointTable( + final ArrayList<Entry<Integer, Integer>> codePointOccurrenceArray) { + final StringBuilder codePointTableString = new StringBuilder(); + int currentCodePointTableIndex = FormatSpec.MINIMAL_ONE_BYTE_CHARACTER_VALUE; + for (final Entry<Integer, Integer> entry : codePointOccurrenceArray) { + // Native reads the table as a string + codePointTableString.appendCodePoint(entry.getKey()); + if (FormatSpec.MAXIMAL_ONE_BYTE_CHARACTER_VALUE < ++currentCodePointTableIndex) { + break; + } + } + return codePointTableString.toString(); + } } diff --git a/tests/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java index 9c3b37387..da1b32a8b 100644 --- a/tests/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java +++ b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java @@ -17,7 +17,7 @@ package com.android.inputmethod.latin.makedict; import com.android.inputmethod.annotations.UsedForTesting; -import com.android.inputmethod.latin.Constants; +import com.android.inputmethod.latin.define.DecoderSpecificConstants; import com.android.inputmethod.latin.makedict.DictDecoder.DictionaryBufferFactory; import java.io.File; @@ -43,22 +43,12 @@ public final class BinaryDictIOUtils { */ public static DictDecoder getDictDecoder(final File dictFile, final long offset, final long length, final int bufferType) { - if (dictFile.isDirectory()) { - return new Ver4DictDecoder(dictFile, bufferType); - } else if (dictFile.isFile()) { - return new Ver2DictDecoder(dictFile, offset, length, bufferType); - } - return null; + return new Ver4DictDecoder(dictFile); } public static DictDecoder getDictDecoder(final File dictFile, final long offset, final long length, final DictionaryBufferFactory factory) { - if (dictFile.isDirectory()) { - return new Ver4DictDecoder(dictFile, factory); - } else if (dictFile.isFile()) { - return new Ver2DictDecoder(dictFile, offset, length, factory); - } - return null; + return new Ver4DictDecoder(dictFile); } public static DictDecoder getDictDecoder(final File dictFile, final long offset, @@ -183,7 +173,7 @@ public final class BinaryDictIOUtils { dictDecoder.readHeader(); int wordPos = 0; final int wordLen = word.codePointCount(0, word.length()); - for (int depth = 0; depth < Constants.DICTIONARY_MAX_WORD_LENGTH; ++depth) { + for (int depth = 0; depth < DecoderSpecificConstants.DICTIONARY_MAX_WORD_LENGTH; ++depth) { if (wordPos >= wordLen) return FormatSpec.NOT_VALID_WORD; do { @@ -206,11 +196,7 @@ public final class BinaryDictIOUtils { if (same) { // found the PtNode matches the word. if (wordPos + currentInfo.mCharacters.length == wordLen) { - if (!currentInfo.isTerminal()) { - return FormatSpec.NOT_VALID_WORD; - } else { - return ptNodePos; - } + return currentInfo.isTerminal() ? ptNodePos : FormatSpec.NOT_VALID_WORD; } wordPos += currentInfo.mCharacters.length; if (currentInfo.mChildrenAddress == FormatSpec.NO_CHILDREN_ADDRESS) { diff --git a/tests/src/com/android/inputmethod/latin/makedict/BinaryDictUtils.java b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictUtils.java deleted file mode 100644 index 5a3eba801..000000000 --- a/tests/src/com/android/inputmethod/latin/makedict/BinaryDictUtils.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * 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.FormatSpec.DictionaryOptions; -import com.android.inputmethod.latin.makedict.FormatSpec.FormatOptions; - -import java.io.File; -import java.util.HashMap; - -public class BinaryDictUtils { - public static final int USE_BYTE_ARRAY = 1; - public static final int USE_BYTE_BUFFER = 2; - - public static final String TEST_DICT_FILE_EXTENSION = ".testDict"; - - public static final FormatSpec.FormatOptions VERSION2_OPTIONS = - new FormatSpec.FormatOptions(FormatSpec.VERSION2); - public static final FormatSpec.FormatOptions VERSION4_OPTIONS_WITHOUT_TIMESTAMP = - new FormatSpec.FormatOptions(FormatSpec.VERSION4, false /* hasTimestamp */); - public static final FormatSpec.FormatOptions VERSION4_OPTIONS_WITH_TIMESTAMP = - new FormatSpec.FormatOptions(FormatSpec.VERSION4, true /* hasTimestamp */); - - public static DictionaryOptions makeDictionaryOptions(final String id, final String version, - final FormatSpec.FormatOptions formatOptions) { - final DictionaryOptions options = new DictionaryOptions(new HashMap<String, String>()); - options.mAttributes.put(DictionaryHeader.DICTIONARY_LOCALE_KEY, "en_US"); - options.mAttributes.put(DictionaryHeader.DICTIONARY_ID_KEY, id); - options.mAttributes.put(DictionaryHeader.DICTIONARY_VERSION_KEY, version); - if (formatOptions.mHasTimestamp) { - options.mAttributes.put(DictionaryHeader.HAS_HISTORICAL_INFO_KEY, - DictionaryHeader.ATTRIBUTE_VALUE_TRUE); - options.mAttributes.put(DictionaryHeader.USES_FORGETTING_CURVE_KEY, - DictionaryHeader.ATTRIBUTE_VALUE_TRUE); - } - return options; - } - - public static File getDictFile(final String name, final String version, - final FormatOptions formatOptions, final File directory) { - if (formatOptions.mVersion == FormatSpec.VERSION2) { - return new File(directory, name + "." + version + TEST_DICT_FILE_EXTENSION); - } else if (formatOptions.mVersion == FormatSpec.VERSION4) { - return new File(directory, name + "." + version); - } else { - throw new RuntimeException("the format option has a wrong version : " - + formatOptions.mVersion); - } - } - - public static DictEncoder getDictEncoder(final File file, final FormatOptions formatOptions) { - if (formatOptions.mVersion == FormatSpec.VERSION4) { - if (!file.isDirectory()) { - file.mkdir(); - } - return new Ver4DictEncoder(file); - } else if (formatOptions.mVersion == FormatSpec.VERSION2) { - return new Ver2DictEncoder(file); - } else { - throw new RuntimeException("The format option has a wrong version : " - + formatOptions.mVersion); - } - } -} diff --git a/tests/src/com/android/inputmethod/latin/makedict/CodePointUtils.java b/tests/src/com/android/inputmethod/latin/makedict/CodePointUtils.java deleted file mode 100644 index a270ee774..000000000 --- a/tests/src/com/android/inputmethod/latin/makedict/CodePointUtils.java +++ /dev/null @@ -1,101 +0,0 @@ -/* - * 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 final int[] LATIN_ALPHABETS_LOWER = { - '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', - 0x00E0 /* LATIN SMALL LETTER A WITH GRAVE */, - 0x00E1 /* LATIN SMALL LETTER A WITH ACUTE */, - 0x00E2 /* LATIN SMALL LETTER A WITH CIRCUMFLEX */, - 0x00E3 /* LATIN SMALL LETTER A WITH TILDE */, - 0x00E4 /* LATIN SMALL LETTER A WITH DIAERESIS */, - 0x00E5 /* LATIN SMALL LETTER A WITH RING ABOVE */, - 0x00E6 /* LATIN SMALL LETTER AE */, - 0x00E7 /* LATIN SMALL LETTER C WITH CEDILLA */, - 0x00E8 /* LATIN SMALL LETTER E WITH GRAVE */, - 0x00E9 /* LATIN SMALL LETTER E WITH ACUTE */, - 0x00EA /* LATIN SMALL LETTER E WITH CIRCUMFLEX */, - 0x00EB /* LATIN SMALL LETTER E WITH DIAERESIS */, - 0x00EC /* LATIN SMALL LETTER I WITH GRAVE */, - 0x00ED /* LATIN SMALL LETTER I WITH ACUTE */, - 0x00EE /* LATIN SMALL LETTER I WITH CIRCUMFLEX */, - 0x00EF /* LATIN SMALL LETTER I WITH DIAERESIS */, - 0x00F0 /* LATIN SMALL LETTER ETH */, - 0x00F1 /* LATIN SMALL LETTER N WITH TILDE */, - 0x00F2 /* LATIN SMALL LETTER O WITH GRAVE */, - 0x00F3 /* LATIN SMALL LETTER O WITH ACUTE */, - 0x00F4 /* LATIN SMALL LETTER O WITH CIRCUMFLEX */, - 0x00F5 /* LATIN SMALL LETTER O WITH TILDE */, - 0x00F6 /* LATIN SMALL LETTER O WITH DIAERESIS */, - 0x00F7 /* LATIN SMALL LETTER O WITH STROKE */, - 0x00F9 /* LATIN SMALL LETTER U WITH GRAVE */, - 0x00FA /* LATIN SMALL LETTER U WITH ACUTE */, - 0x00FB /* LATIN SMALL LETTER U WITH CIRCUMFLEX */, - 0x00FC /* LATIN SMALL LETTER U WITH DIAERESIS */, - 0x00FD /* LATIN SMALL LETTER Y WITH ACUTE */, - 0x00FE /* LATIN SMALL LETTER THORN */, - 0x00FF /* LATIN SMALL LETTER Y WITH DIAERESIS */ - }; - - 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/DictEncoder.java b/tests/src/com/android/inputmethod/latin/makedict/DictEncoder.java index 678c5ca6b..10dd00325 100644 --- a/tests/src/com/android/inputmethod/latin/makedict/DictEncoder.java +++ b/tests/src/com/android/inputmethod/latin/makedict/DictEncoder.java @@ -21,6 +21,7 @@ import com.android.inputmethod.latin.makedict.FormatSpec.FormatOptions; import com.android.inputmethod.latin.makedict.FusionDictionary.PtNode; import java.io.IOException; +import java.util.HashMap; /** * An interface of binary dictionary encoder. @@ -33,6 +34,6 @@ public interface DictEncoder { public void setPosition(final int position); public int getPosition(); public void writePtNodeCount(final int ptNodeCount); - public void writeForwardLinkAddress(final int forwardLinkAddress); - public void writePtNode(final PtNode ptNode, final FusionDictionary dict); + public void writePtNode(final PtNode ptNode, final FusionDictionary dict, + final HashMap<Integer, Integer> codePointToOneByteCodeMap); } diff --git a/tests/src/com/android/inputmethod/latin/makedict/FusionDictionary.java b/tests/src/com/android/inputmethod/latin/makedict/FusionDictionary.java index 4a8c178b5..52060bed2 100644 --- a/tests/src/com/android/inputmethod/latin/makedict/FusionDictionary.java +++ b/tests/src/com/android/inputmethod/latin/makedict/FusionDictionary.java @@ -17,7 +17,7 @@ package com.android.inputmethod.latin.makedict; import com.android.inputmethod.annotations.UsedForTesting; -import com.android.inputmethod.latin.Constants; +import com.android.inputmethod.latin.define.DecoderSpecificConstants; import com.android.inputmethod.latin.makedict.FormatSpec.DictionaryOptions; import java.util.ArrayList; @@ -82,14 +82,13 @@ public final class FusionDictionary implements Iterable<WordProperty> { public static final class PtNode { private static final int NOT_A_TERMINAL = -1; final int mChars[]; - ArrayList<WeightedString> mShortcutTargets; ArrayList<WeightedString> mBigrams; // null == mProbabilityInfo indicates this is not a terminal. ProbabilityInfo mProbabilityInfo; int mTerminalId; // NOT_A_TERMINAL == mTerminalId indicates this is not a terminal. PtNodeArray mChildren; boolean mIsNotAWord; // Only a shortcut - boolean mIsBlacklistEntry; + boolean mIsPossiblyOffensive; // mCachedSize and mCachedAddressBefore/AfterUpdate are helpers for binary dictionary // generation. Before and After always hold the same value except during dictionary // address compression, where the update process needs to know about both values at the @@ -100,30 +99,27 @@ public final class FusionDictionary implements Iterable<WordProperty> { int mCachedAddressBeforeUpdate; // The address of this PtNode (before update) int mCachedAddressAfterUpdate; // The address of this PtNode (after update) - public PtNode(final int[] chars, final ArrayList<WeightedString> shortcutTargets, - final ArrayList<WeightedString> bigrams, final ProbabilityInfo probabilityInfo, - final boolean isNotAWord, final boolean isBlacklistEntry) { + public PtNode(final int[] chars, final ArrayList<WeightedString> bigrams, + final ProbabilityInfo probabilityInfo, final boolean isNotAWord, + final boolean isPossiblyOffensive) { mChars = chars; mProbabilityInfo = probabilityInfo; mTerminalId = probabilityInfo == null ? NOT_A_TERMINAL : probabilityInfo.mProbability; - mShortcutTargets = shortcutTargets; mBigrams = bigrams; mChildren = null; mIsNotAWord = isNotAWord; - mIsBlacklistEntry = isBlacklistEntry; + mIsPossiblyOffensive = isPossiblyOffensive; } - public PtNode(final int[] chars, final ArrayList<WeightedString> shortcutTargets, - final ArrayList<WeightedString> bigrams, final ProbabilityInfo probabilityInfo, - final boolean isNotAWord, final boolean isBlacklistEntry, - final PtNodeArray children) { + public PtNode(final int[] chars, final ArrayList<WeightedString> bigrams, + final ProbabilityInfo probabilityInfo, final boolean isNotAWord, + final boolean isPossiblyOffensive, final PtNodeArray children) { mChars = chars; mProbabilityInfo = probabilityInfo; - mShortcutTargets = shortcutTargets; mBigrams = bigrams; mChildren = children; mIsNotAWord = isNotAWord; - mIsBlacklistEntry = isBlacklistEntry; + mIsPossiblyOffensive = isPossiblyOffensive; } public void addChild(PtNode n) { @@ -142,27 +138,15 @@ public final class FusionDictionary implements Iterable<WordProperty> { } public int getProbability() { - if (isTerminal()) { - return mProbabilityInfo.mProbability; - } else { - return NOT_A_TERMINAL; - } + return isTerminal() ? mProbabilityInfo.mProbability : NOT_A_TERMINAL; } public boolean getIsNotAWord() { return mIsNotAWord; } - public boolean getIsBlacklistEntry() { - return mIsBlacklistEntry; - } - - public ArrayList<WeightedString> getShortcutTargets() { - // We don't want write permission to escape outside the package, so we return a copy - if (null == mShortcutTargets) return null; - final ArrayList<WeightedString> copyOfShortcutTargets = - new ArrayList<>(mShortcutTargets); - return copyOfShortcutTargets; + public boolean getIsPossiblyOffensive() { + return mIsPossiblyOffensive; } public ArrayList<WeightedString> getBigrams() { @@ -195,24 +179,6 @@ public final class FusionDictionary implements Iterable<WordProperty> { } /** - * Gets the shortcut target for the given word. Returns null if the word is not in the - * shortcut list. - */ - public WeightedString getShortcut(final String word) { - // TODO: Don't do a linear search - if (mShortcutTargets != null) { - final int size = mShortcutTargets.size(); - for (int i = 0; i < size; ++i) { - WeightedString shortcut = mShortcutTargets.get(i); - if (shortcut.mWord.equals(word)) { - return shortcut; - } - } - } - return null; - } - - /** * Gets the bigram for the given word. * Returns null if the word is not in the bigrams list. */ @@ -235,28 +201,10 @@ public final class FusionDictionary implements Iterable<WordProperty> { * the existing ones if any. Note: unigram, bigram, and shortcut frequencies are only * updated if they are higher than the existing ones. */ - private void update(final ProbabilityInfo probabilityInfo, - final ArrayList<WeightedString> shortcutTargets, + void update(final ProbabilityInfo probabilityInfo, final ArrayList<WeightedString> bigrams, - final boolean isNotAWord, final boolean isBlacklistEntry) { + final boolean isNotAWord, final boolean isPossiblyOffensive) { mProbabilityInfo = ProbabilityInfo.max(mProbabilityInfo, probabilityInfo); - if (shortcutTargets != null) { - if (mShortcutTargets == null) { - mShortcutTargets = shortcutTargets; - } else { - final int size = shortcutTargets.size(); - for (int i = 0; i < size; ++i) { - final WeightedString shortcut = shortcutTargets.get(i); - final WeightedString existingShortcut = getShortcut(shortcut.mWord); - if (existingShortcut == null) { - mShortcutTargets.add(shortcut); - } else { - existingShortcut.mProbabilityInfo = ProbabilityInfo.max( - existingShortcut.mProbabilityInfo, shortcut.mProbabilityInfo); - } - } - } - } if (bigrams != null) { if (mBigrams == null) { mBigrams = bigrams; @@ -275,7 +223,7 @@ public final class FusionDictionary implements Iterable<WordProperty> { } } mIsNotAWord = isNotAWord; - mIsBlacklistEntry = isBlacklistEntry; + mIsPossiblyOffensive = isPossiblyOffensive; } } @@ -316,31 +264,16 @@ public final class FusionDictionary implements Iterable<WordProperty> { * Helper method to add a word as a string. * * This method adds a word to the dictionary with the given frequency. Optional - * lists of bigrams and shortcuts can be passed here. For each word inside, + * lists of bigrams can be passed here. For each word inside, * they will be added to the dictionary as necessary. - * - * @param word the word to add. + * @param word the word to add. * @param probabilityInfo probability information of the word. - * @param shortcutTargets a list of shortcut targets for this word, or null. * @param isNotAWord true if this should not be considered a word (e.g. shortcut only) + * @param isPossiblyOffensive true if this word is possibly offensive */ public void add(final String word, final ProbabilityInfo probabilityInfo, - final ArrayList<WeightedString> shortcutTargets, final boolean isNotAWord) { - add(getCodePoints(word), probabilityInfo, shortcutTargets, isNotAWord, - false /* isBlacklistEntry */); - } - - /** - * Helper method to add a blacklist entry as a string. - * - * @param word the word to add as a blacklist entry. - * @param shortcutTargets a list of shortcut targets for this word, or null. - * @param isNotAWord true if this is not a word for spellcheking purposes (shortcut only or so) - */ - public void addBlacklistEntry(final String word, - final ArrayList<WeightedString> shortcutTargets, final boolean isNotAWord) { - add(getCodePoints(word), new ProbabilityInfo(0), shortcutTargets, isNotAWord, - true /* isBlacklistEntry */); + final boolean isNotAWord, final boolean isPossiblyOffensive) { + add(getCodePoints(word), probabilityInfo, isNotAWord, isPossiblyOffensive); } /** @@ -349,15 +282,15 @@ public final class FusionDictionary implements Iterable<WordProperty> { * This method checks that all PtNodes in a node array are ordered as expected. * If they are, nothing happens. If they aren't, an exception is thrown. */ - private void checkStack(PtNodeArray ptNodeArray) { + private static void checkStack(PtNodeArray ptNodeArray) { ArrayList<PtNode> stack = ptNodeArray.mData; int lastValue = -1; for (int i = 0; i < stack.size(); ++i) { int currentValue = stack.get(i).mChars[0]; - if (currentValue <= lastValue) + if (currentValue <= lastValue) { throw new RuntimeException("Invalid stack"); - else - lastValue = currentValue; + } + lastValue = currentValue; } } @@ -374,8 +307,8 @@ public final class FusionDictionary implements Iterable<WordProperty> { if (ptNode0 != null) { final PtNode ptNode1 = findWordInTree(mRootNodeArray, word1); if (ptNode1 == null) { - add(getCodePoints(word1), new ProbabilityInfo(0), null, false /* isNotAWord */, - false /* isBlacklistEntry */); + add(getCodePoints(word1), new ProbabilityInfo(0), false /* isNotAWord */, + false /* isPossiblyOffensive */); // The PtNode for the first word may have moved by the above insertion, // if word1 and word2 share a common stem that happens not to have been // a cutting point until now. In this case, we need to refresh ptNode. @@ -392,18 +325,15 @@ public final class FusionDictionary implements Iterable<WordProperty> { * * The shortcuts, if any, have to be in the dictionary already. If they aren't, * an exception is thrown. - * - * @param word the word, as an int array. + * @param word the word, as an int array. * @param probabilityInfo the probability information of the word. - * @param shortcutTargets an optional list of shortcut targets for this word (null if none). * @param isNotAWord true if this is not a word for spellcheking purposes (shortcut only or so) - * @param isBlacklistEntry true if this is a blacklisted word, false otherwise + * @param isPossiblyOffensive true if this word is possibly offensive */ private void add(final int[] word, final ProbabilityInfo probabilityInfo, - final ArrayList<WeightedString> shortcutTargets, - final boolean isNotAWord, final boolean isBlacklistEntry) { + final boolean isNotAWord, final boolean isPossiblyOffensive) { assert(probabilityInfo.mProbability <= FormatSpec.MAX_TERMINAL_FREQUENCY); - if (word.length >= Constants.DICTIONARY_MAX_WORD_LENGTH) { + if (word.length >= DecoderSpecificConstants.DICTIONARY_MAX_WORD_LENGTH) { MakedictLog.w("Ignoring a word that is too long: word.length = " + word.length); return; } @@ -430,8 +360,8 @@ public final class FusionDictionary implements Iterable<WordProperty> { // No node at this point to accept the word. Create one. final int insertionIndex = findInsertionIndex(currentNodeArray, word[charIndex]); final PtNode newPtNode = new PtNode(Arrays.copyOfRange(word, charIndex, word.length), - shortcutTargets, null /* bigrams */, probabilityInfo, isNotAWord, - isBlacklistEntry); + null /* bigrams */, probabilityInfo, isNotAWord, + isPossiblyOffensive); currentNodeArray.mData.add(insertionIndex, newPtNode); if (DBG) checkStack(currentNodeArray); } else { @@ -441,15 +371,15 @@ public final class FusionDictionary implements Iterable<WordProperty> { // The new word is a prefix of an existing word, but the node on which it // should end already exists as is. Since the old PtNode was not a terminal, // make it one by filling in its frequency and other attributes - currentPtNode.update(probabilityInfo, shortcutTargets, null, isNotAWord, - isBlacklistEntry); + currentPtNode.update(probabilityInfo, null, isNotAWord, + isPossiblyOffensive); } else { // The new word matches the full old word and extends past it. // We only have to create a new node and add it to the end of this. final PtNode newNode = new PtNode( Arrays.copyOfRange(word, charIndex + differentCharIndex, word.length), - shortcutTargets, null /* bigrams */, probabilityInfo, - isNotAWord, isBlacklistEntry); + null /* bigrams */, probabilityInfo, + isNotAWord, isPossiblyOffensive); currentPtNode.mChildren = new PtNodeArray(); currentPtNode.mChildren.mData.add(newNode); } @@ -457,18 +387,18 @@ public final class FusionDictionary implements Iterable<WordProperty> { if (0 == differentCharIndex) { // Exact same word. Update the frequency if higher. This will also add the // new shortcuts to the existing shortcut list if it already exists. - currentPtNode.update(probabilityInfo, shortcutTargets, null, + currentPtNode.update(probabilityInfo, null, currentPtNode.mIsNotAWord && isNotAWord, - currentPtNode.mIsBlacklistEntry || isBlacklistEntry); + currentPtNode.mIsPossiblyOffensive || isPossiblyOffensive); } else { // Partial prefix match only. We have to replace the current node with a node // containing the current prefix and create two new ones for the tails. PtNodeArray newChildren = new PtNodeArray(); final PtNode newOldWord = new PtNode( Arrays.copyOfRange(currentPtNode.mChars, differentCharIndex, - currentPtNode.mChars.length), currentPtNode.mShortcutTargets, + currentPtNode.mChars.length), currentPtNode.mBigrams, currentPtNode.mProbabilityInfo, - currentPtNode.mIsNotAWord, currentPtNode.mIsBlacklistEntry, + currentPtNode.mIsNotAWord, currentPtNode.mIsPossiblyOffensive, currentPtNode.mChildren); newChildren.mData.add(newOldWord); @@ -476,18 +406,18 @@ public final class FusionDictionary implements Iterable<WordProperty> { if (charIndex + differentCharIndex >= word.length) { newParent = new PtNode( Arrays.copyOfRange(currentPtNode.mChars, 0, differentCharIndex), - shortcutTargets, null /* bigrams */, probabilityInfo, - isNotAWord, isBlacklistEntry, newChildren); + null /* bigrams */, probabilityInfo, + isNotAWord, isPossiblyOffensive, newChildren); } else { newParent = new PtNode( Arrays.copyOfRange(currentPtNode.mChars, 0, differentCharIndex), - null /* shortcutTargets */, null /* bigrams */, - null /* probabilityInfo */, false /* isNotAWord */, - false /* isBlacklistEntry */, newChildren); + null /* bigrams */, null /* probabilityInfo */, + false /* isNotAWord */, false /* isPossiblyOffensive */, + newChildren); final PtNode newWord = new PtNode(Arrays.copyOfRange(word, charIndex + differentCharIndex, word.length), - shortcutTargets, null /* bigrams */, probabilityInfo, - isNotAWord, isBlacklistEntry); + null /* bigrams */, probabilityInfo, + isNotAWord, isPossiblyOffensive); final int addIndex = word[charIndex + differentCharIndex] > currentPtNode.mChars[differentCharIndex] ? 1 : 0; newChildren.mData.add(addIndex, newWord); @@ -533,14 +463,14 @@ public final class FusionDictionary implements Iterable<WordProperty> { * is ignored. * This comparator imposes orderings that are inconsistent with equals. */ - static private final class PtNodeComparator implements java.util.Comparator<PtNode> { + static final class PtNodeComparator implements java.util.Comparator<PtNode> { @Override public int compare(PtNode p1, PtNode p2) { if (p1.mChars[0] == p2.mChars[0]) return 0; return p1.mChars[0] < p2.mChars[0] ? -1 : 1; } } - final static private PtNodeComparator PTNODE_COMPARATOR = new PtNodeComparator(); + final static PtNodeComparator PTNODE_COMPARATOR = new PtNodeComparator(); /** * Finds the insertion index of a character within a node array. @@ -548,8 +478,8 @@ public final class FusionDictionary implements Iterable<WordProperty> { private static int findInsertionIndex(final PtNodeArray nodeArray, int character) { final ArrayList<PtNode> data = nodeArray.mData; final PtNode reference = new PtNode(new int[] { character }, - null /* shortcutTargets */, null /* bigrams */, null /* probabilityInfo */, - false /* isNotAWord */, false /* isBlacklistEntry */); + null /* bigrams */, null /* probabilityInfo */, + false /* isNotAWord */, false /* isPossiblyOffensive */); int result = Collections.binarySearch(data, reference, PTNODE_COMPARATOR); return result >= 0 ? result : -result - 1; } @@ -571,7 +501,8 @@ public final class FusionDictionary implements Iterable<WordProperty> { /** * Helper method to find a word in a given branch. */ - public static PtNode findWordInTree(PtNodeArray nodeArray, final String string) { + public static PtNode findWordInTree(final PtNodeArray rootNodeArray, final String string) { + PtNodeArray nodeArray = rootNodeArray; int index = 0; final StringBuilder checker = DBG ? new StringBuilder() : null; final int[] codePoints = getCodePoints(string); @@ -684,9 +615,8 @@ public final class FusionDictionary implements Iterable<WordProperty> { } if (currentPtNode.isTerminal()) { return new WordProperty(mCurrentString.toString(), - currentPtNode.mProbabilityInfo, - currentPtNode.mShortcutTargets, currentPtNode.mBigrams, - currentPtNode.mIsNotAWord, currentPtNode.mIsBlacklistEntry); + currentPtNode.mProbabilityInfo, currentPtNode.mBigrams, + currentPtNode.mIsNotAWord, currentPtNode.mIsPossiblyOffensive); } } else { mPositions.removeLast(); diff --git a/tests/src/com/android/inputmethod/latin/makedict/Ver2DictDecoder.java b/tests/src/com/android/inputmethod/latin/makedict/Ver2DictDecoder.java deleted file mode 100644 index 65b84d5f7..000000000 --- a/tests/src/com/android/inputmethod/latin/makedict/Ver2DictDecoder.java +++ /dev/null @@ -1,321 +0,0 @@ -/* - * 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.annotations.UsedForTesting; -import com.android.inputmethod.latin.BinaryDictionary; -import com.android.inputmethod.latin.makedict.BinaryDictDecoderUtils.CharEncoding; -import com.android.inputmethod.latin.makedict.BinaryDictDecoderUtils.DictBuffer; - -import java.io.File; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Arrays; - -/** - * An implementation of DictDecoder for version 2 binary dictionary. - */ -// TODO: Separate logics that are used only for testing. -@UsedForTesting -public class Ver2DictDecoder extends AbstractDictDecoder { - /** - * A utility class for reading a PtNode. - */ - protected static class PtNodeReader { - private static ProbabilityInfo readProbabilityInfo(final DictBuffer dictBuffer) { - // Ver2 dicts don't contain historical information. - return new ProbabilityInfo(dictBuffer.readUnsignedByte()); - } - - protected static int readPtNodeOptionFlags(final DictBuffer dictBuffer) { - return dictBuffer.readUnsignedByte(); - } - - protected static int readChildrenAddress(final DictBuffer dictBuffer, - final int ptNodeFlags) { - switch (ptNodeFlags & FormatSpec.MASK_CHILDREN_ADDRESS_TYPE) { - case FormatSpec.FLAG_CHILDREN_ADDRESS_TYPE_ONEBYTE: - return dictBuffer.readUnsignedByte(); - case FormatSpec.FLAG_CHILDREN_ADDRESS_TYPE_TWOBYTES: - return dictBuffer.readUnsignedShort(); - case FormatSpec.FLAG_CHILDREN_ADDRESS_TYPE_THREEBYTES: - return dictBuffer.readUnsignedInt24(); - case FormatSpec.FLAG_CHILDREN_ADDRESS_TYPE_NOADDRESS: - default: - return FormatSpec.NO_CHILDREN_ADDRESS; - } - } - - // Reads shortcuts and returns the read length. - protected static int readShortcut(final DictBuffer dictBuffer, - final ArrayList<WeightedString> shortcutTargets) { - final int pointerBefore = dictBuffer.position(); - dictBuffer.readUnsignedShort(); // skip the size - while (true) { - final int targetFlags = dictBuffer.readUnsignedByte(); - final String word = CharEncoding.readString(dictBuffer); - shortcutTargets.add(new WeightedString(word, - targetFlags & FormatSpec.FLAG_BIGRAM_SHORTCUT_ATTR_FREQUENCY)); - if (0 == (targetFlags & FormatSpec.FLAG_BIGRAM_SHORTCUT_ATTR_HAS_NEXT)) break; - } - return dictBuffer.position() - pointerBefore; - } - - protected static int readBigramAddresses(final DictBuffer dictBuffer, - final ArrayList<PendingAttribute> bigrams, final int baseAddress) { - int readLength = 0; - int bigramCount = 0; - while (bigramCount++ < FormatSpec.MAX_BIGRAMS_IN_A_PTNODE) { - final int bigramFlags = dictBuffer.readUnsignedByte(); - ++readLength; - final int sign = 0 == (bigramFlags & FormatSpec.FLAG_BIGRAM_ATTR_OFFSET_NEGATIVE) - ? 1 : -1; - int bigramAddress = baseAddress + readLength; - switch (bigramFlags & FormatSpec.MASK_BIGRAM_ATTR_ADDRESS_TYPE) { - case FormatSpec.FLAG_BIGRAM_ATTR_ADDRESS_TYPE_ONEBYTE: - bigramAddress += sign * dictBuffer.readUnsignedByte(); - readLength += 1; - break; - case FormatSpec.FLAG_BIGRAM_ATTR_ADDRESS_TYPE_TWOBYTES: - bigramAddress += sign * dictBuffer.readUnsignedShort(); - readLength += 2; - break; - case FormatSpec.FLAG_BIGRAM_ATTR_ADDRESS_TYPE_THREEBYTES: - bigramAddress += sign * dictBuffer.readUnsignedInt24(); - readLength += 3; - break; - default: - throw new RuntimeException("Has bigrams with no address"); - } - bigrams.add(new PendingAttribute( - bigramFlags & FormatSpec.FLAG_BIGRAM_SHORTCUT_ATTR_FREQUENCY, - bigramAddress)); - if (0 == (bigramFlags & FormatSpec.FLAG_BIGRAM_SHORTCUT_ATTR_HAS_NEXT)) break; - } - return readLength; - } - } - - protected final File mDictionaryBinaryFile; - protected final long mOffset; - protected final long mLength; - // TODO: Remove mBufferFactory and mDictBuffer from this class members because they are now - // used only for testing. - private final DictionaryBufferFactory mBufferFactory; - protected DictBuffer mDictBuffer; - - @UsedForTesting - /* package */ Ver2DictDecoder(final File file, final long offset, final long length, - final int factoryFlag) { - mDictionaryBinaryFile = file; - mOffset = offset; - mLength = length; - mDictBuffer = null; - if ((factoryFlag & MASK_DICTBUFFER) == USE_READONLY_BYTEBUFFER) { - mBufferFactory = new DictionaryBufferFromReadOnlyByteBufferFactory(); - } else if ((factoryFlag & MASK_DICTBUFFER) == USE_BYTEARRAY) { - mBufferFactory = new DictionaryBufferFromByteArrayFactory(); - } else if ((factoryFlag & MASK_DICTBUFFER) == USE_WRITABLE_BYTEBUFFER) { - mBufferFactory = new DictionaryBufferFromWritableByteBufferFactory(); - } else { - mBufferFactory = new DictionaryBufferFromReadOnlyByteBufferFactory(); - } - } - - /* package */ Ver2DictDecoder(final File file, final long offset, final long length, - final DictionaryBufferFactory factory) { - mDictionaryBinaryFile = file; - mOffset = offset; - mLength = length; - mBufferFactory = factory; - } - - @Override - public void openDictBuffer() throws FileNotFoundException, IOException { - mDictBuffer = mBufferFactory.getDictionaryBuffer(mDictionaryBinaryFile); - } - - @Override - public boolean isDictBufferOpen() { - return mDictBuffer != null; - } - - /* package */ DictBuffer getDictBuffer() { - return mDictBuffer; - } - - @UsedForTesting - /* package */ DictBuffer openAndGetDictBuffer() throws FileNotFoundException, IOException { - openDictBuffer(); - return getDictBuffer(); - } - - @Override - public DictionaryHeader readHeader() throws IOException, UnsupportedFormatException { - // dictType is not being used in dicttool. Passing an empty string. - final BinaryDictionary binaryDictionary = new BinaryDictionary( - mDictionaryBinaryFile.getAbsolutePath(), mOffset, mLength, - true /* useFullEditDistance */, null /* locale */, "" /* dictType */, - false /* isUpdatable */); - final DictionaryHeader header = binaryDictionary.getHeader(); - binaryDictionary.close(); - if (header == null) { - throw new IOException("Cannot read the dictionary header."); - } - if (header.mFormatOptions.mVersion != FormatSpec.VERSION2) { - throw new UnsupportedFormatException("File header has a wrong version : " - + header.mFormatOptions.mVersion); - } - if (!isDictBufferOpen()) { - openDictBuffer(); - } - // Advance buffer reading position to the head of dictionary body. - setPosition(header.mBodyOffset); - return header; - } - - // TODO: Make this buffer multi thread safe. - private final int[] mCharacterBuffer = new int[FormatSpec.MAX_WORD_LENGTH]; - @Override - public PtNodeInfo readPtNode(final int ptNodePos) { - int addressPointer = ptNodePos; - final int flags = PtNodeReader.readPtNodeOptionFlags(mDictBuffer); - addressPointer += FormatSpec.PTNODE_FLAGS_SIZE; - final int characters[]; - if (0 != (flags & FormatSpec.FLAG_HAS_MULTIPLE_CHARS)) { - int index = 0; - int character = CharEncoding.readChar(mDictBuffer); - addressPointer += CharEncoding.getCharSize(character); - while (FormatSpec.INVALID_CHARACTER != character) { - // FusionDictionary is making sure that the length of the word is smaller than - // MAX_WORD_LENGTH. - // So we'll never write past the end of mCharacterBuffer. - mCharacterBuffer[index++] = character; - character = CharEncoding.readChar(mDictBuffer); - addressPointer += CharEncoding.getCharSize(character); - } - characters = Arrays.copyOfRange(mCharacterBuffer, 0, index); - } else { - final int character = CharEncoding.readChar(mDictBuffer); - addressPointer += CharEncoding.getCharSize(character); - characters = new int[] { character }; - } - final ProbabilityInfo probabilityInfo; - if (0 != (FormatSpec.FLAG_IS_TERMINAL & flags)) { - probabilityInfo = PtNodeReader.readProbabilityInfo(mDictBuffer); - addressPointer += FormatSpec.PTNODE_FREQUENCY_SIZE; - } else { - probabilityInfo = null; - } - int childrenAddress = PtNodeReader.readChildrenAddress(mDictBuffer, flags); - if (childrenAddress != FormatSpec.NO_CHILDREN_ADDRESS) { - childrenAddress += addressPointer; - } - addressPointer += BinaryDictIOUtils.getChildrenAddressSize(flags); - final ArrayList<WeightedString> shortcutTargets; - if (0 != (flags & FormatSpec.FLAG_HAS_SHORTCUT_TARGETS)) { - // readShortcut will add shortcuts to shortcutTargets. - shortcutTargets = new ArrayList<>(); - addressPointer += PtNodeReader.readShortcut(mDictBuffer, shortcutTargets); - } else { - shortcutTargets = null; - } - - final ArrayList<PendingAttribute> bigrams; - if (0 != (flags & FormatSpec.FLAG_HAS_BIGRAMS)) { - bigrams = new ArrayList<>(); - addressPointer += PtNodeReader.readBigramAddresses(mDictBuffer, bigrams, - addressPointer); - if (bigrams.size() >= FormatSpec.MAX_BIGRAMS_IN_A_PTNODE) { - throw new RuntimeException("Too many bigrams in a PtNode (" + bigrams.size() - + " but max is " + FormatSpec.MAX_BIGRAMS_IN_A_PTNODE + ")"); - } - } else { - bigrams = null; - } - return new PtNodeInfo(ptNodePos, addressPointer, flags, characters, probabilityInfo, - childrenAddress, shortcutTargets, bigrams); - } - - @Override - public FusionDictionary readDictionaryBinary(final boolean deleteDictIfBroken) - throws FileNotFoundException, IOException, UnsupportedFormatException { - // dictType is not being used in dicttool. Passing an empty string. - final BinaryDictionary binaryDictionary = new BinaryDictionary( - mDictionaryBinaryFile.getAbsolutePath(), 0 /* offset */, - mDictionaryBinaryFile.length() /* length */, true /* useFullEditDistance */, - null /* locale */, "" /* dictType */, false /* isUpdatable */); - final DictionaryHeader header = readHeader(); - final FusionDictionary fusionDict = - new FusionDictionary(new FusionDictionary.PtNodeArray(), header.mDictionaryOptions); - int token = 0; - final ArrayList<WordProperty> wordProperties = new ArrayList<>(); - do { - final BinaryDictionary.GetNextWordPropertyResult result = - binaryDictionary.getNextWordProperty(token); - final WordProperty wordProperty = result.mWordProperty; - if (wordProperty == null) { - binaryDictionary.close(); - if (deleteDictIfBroken) { - mDictionaryBinaryFile.delete(); - } - return null; - } - wordProperties.add(wordProperty); - token = result.mNextToken; - } while (token != 0); - - // Insert unigrams into the fusion dictionary. - for (final WordProperty wordProperty : wordProperties) { - if (wordProperty.mIsBlacklistEntry) { - fusionDict.addBlacklistEntry(wordProperty.mWord, wordProperty.mShortcutTargets, - wordProperty.mIsNotAWord); - } else { - fusionDict.add(wordProperty.mWord, wordProperty.mProbabilityInfo, - wordProperty.mShortcutTargets, wordProperty.mIsNotAWord); - } - } - // Insert bigrams into the fusion dictionary. - for (final WordProperty wordProperty : wordProperties) { - if (wordProperty.mBigrams == null) { - continue; - } - final String word0 = wordProperty.mWord; - for (final WeightedString bigram : wordProperty.mBigrams) { - fusionDict.setBigram(word0, bigram.mWord, bigram.mProbabilityInfo); - } - } - binaryDictionary.close(); - return fusionDict; - } - - @Override - public void setPosition(int newPos) { - mDictBuffer.position(newPos); - } - - @Override - public int getPosition() { - return mDictBuffer.position(); - } - - @Override - public int readPtNodeCount() { - return BinaryDictDecoderUtils.readPtNodeCount(mDictBuffer); - } -} diff --git a/tests/src/com/android/inputmethod/latin/makedict/Ver2DictDecoderTests.java b/tests/src/com/android/inputmethod/latin/makedict/Ver2DictDecoderTests.java deleted file mode 100644 index 3882c2c55..000000000 --- a/tests/src/com/android/inputmethod/latin/makedict/Ver2DictDecoderTests.java +++ /dev/null @@ -1,150 +0,0 @@ -/* - * 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 Ver2DictDecoder - */ -public class Ver2DictDecoderTests extends AndroidTestCase { - private static final String TAG = Ver2DictDecoderTests.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); - } - } - } - } - - 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 Ver2DictDecoder dictDecoder = new Ver2DictDecoder(testFile, 0, testFile.length(), - 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()); - } - - 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 Ver2DictDecoder dictDecoder = new Ver2DictDecoder(testFile, 0, testFile.length(), - 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/makedict/Ver2DictEncoder.java b/tests/src/com/android/inputmethod/latin/makedict/Ver2DictEncoder.java deleted file mode 100644 index a286190cb..000000000 --- a/tests/src/com/android/inputmethod/latin/makedict/Ver2DictEncoder.java +++ /dev/null @@ -1,240 +0,0 @@ -/* - * 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.annotations.UsedForTesting; -import com.android.inputmethod.latin.makedict.BinaryDictDecoderUtils.CharEncoding; -import com.android.inputmethod.latin.makedict.FormatSpec.FormatOptions; -import com.android.inputmethod.latin.makedict.FusionDictionary.PtNode; -import com.android.inputmethod.latin.makedict.FusionDictionary.PtNodeArray; - -import java.io.File; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.OutputStream; -import java.util.ArrayList; -import java.util.Iterator; - -/** - * An implementation of DictEncoder for version 2 binary dictionary. - */ -@UsedForTesting -public class Ver2DictEncoder implements DictEncoder { - - private final File mDictFile; - private OutputStream mOutStream; - private byte[] mBuffer; - private int mPosition; - - @UsedForTesting - public Ver2DictEncoder(final File dictFile) { - mDictFile = dictFile; - mOutStream = null; - mBuffer = null; - } - - // This constructor is used only by BinaryDictOffdeviceUtilsTests. - // If you want to use this in the production code, you should consider keeping consistency of - // the interface of Ver3DictDecoder by using factory. - @UsedForTesting - public Ver2DictEncoder(final OutputStream outStream) { - mDictFile = null; - mOutStream = outStream; - } - - private void openStream() throws FileNotFoundException { - mOutStream = new FileOutputStream(mDictFile); - } - - private void close() throws IOException { - if (mOutStream != null) { - mOutStream.close(); - mOutStream = null; - } - } - - @Override - public void writeDictionary(final FusionDictionary dict, final FormatOptions formatOptions) - throws IOException, UnsupportedFormatException { - if (formatOptions.mVersion > FormatSpec.VERSION2) { - throw new UnsupportedFormatException( - "The given format options has wrong version number : " - + formatOptions.mVersion); - } - - if (mOutStream == null) { - openStream(); - } - BinaryDictEncoderUtils.writeDictionaryHeader(mOutStream, dict, formatOptions); - - // Addresses are limited to 3 bytes, but since addresses can be relative to each node - // array, the structure itself is not limited to 16MB. However, if it is over 16MB deciding - // the order of the PtNode arrays becomes a quite complicated problem, because though the - // dictionary itself does not have a size limit, each node array must still be within 16MB - // of all its children and parents. As long as this is ensured, the dictionary file may - // grow to any size. - - // Leave the choice of the optimal node order to the flattenTree function. - MakedictLog.i("Flattening the tree..."); - ArrayList<PtNodeArray> flatNodes = BinaryDictEncoderUtils.flattenTree(dict.mRootNodeArray); - - MakedictLog.i("Computing addresses..."); - BinaryDictEncoderUtils.computeAddresses(dict, flatNodes); - MakedictLog.i("Checking PtNode array..."); - if (MakedictLog.DBG) BinaryDictEncoderUtils.checkFlatPtNodeArrayList(flatNodes); - - // Create a buffer that matches the final dictionary size. - final PtNodeArray lastNodeArray = flatNodes.get(flatNodes.size() - 1); - final int bufferSize = lastNodeArray.mCachedAddressAfterUpdate + lastNodeArray.mCachedSize; - mBuffer = new byte[bufferSize]; - - MakedictLog.i("Writing file..."); - - for (PtNodeArray nodeArray : flatNodes) { - BinaryDictEncoderUtils.writePlacedPtNodeArray(dict, this, nodeArray); - } - if (MakedictLog.DBG) BinaryDictEncoderUtils.showStatistics(flatNodes); - mOutStream.write(mBuffer, 0, mPosition); - - MakedictLog.i("Done"); - close(); - } - - @Override - public void setPosition(final int position) { - if (mBuffer == null || position < 0 || position >= mBuffer.length) return; - mPosition = position; - } - - @Override - public int getPosition() { - return mPosition; - } - - @Override - public void writePtNodeCount(final int ptNodeCount) { - final int countSize = BinaryDictIOUtils.getPtNodeCountSize(ptNodeCount); - if (countSize != 1 && countSize != 2) { - throw new RuntimeException("Strange size from getGroupCountSize : " + countSize); - } - final int encodedPtNodeCount = (countSize == 2) ? - (ptNodeCount | FormatSpec.LARGE_PTNODE_ARRAY_SIZE_FIELD_SIZE_FLAG) : ptNodeCount; - mPosition = BinaryDictEncoderUtils.writeUIntToBuffer(mBuffer, mPosition, encodedPtNodeCount, - countSize); - } - - private void writePtNodeFlags(final PtNode ptNode) { - final int childrenPos = BinaryDictEncoderUtils.getChildrenPosition(ptNode); - mPosition = BinaryDictEncoderUtils.writeUIntToBuffer(mBuffer, mPosition, - BinaryDictEncoderUtils.makePtNodeFlags(ptNode, childrenPos), - FormatSpec.PTNODE_FLAGS_SIZE); - } - - private void writeCharacters(final int[] codePoints, final boolean hasSeveralChars) { - mPosition = CharEncoding.writeCharArray(codePoints, mBuffer, mPosition); - if (hasSeveralChars) { - mBuffer[mPosition++] = FormatSpec.PTNODE_CHARACTERS_TERMINATOR; - } - } - - private void writeFrequency(final int frequency) { - if (frequency >= 0) { - mPosition = BinaryDictEncoderUtils.writeUIntToBuffer(mBuffer, mPosition, frequency, - FormatSpec.PTNODE_FREQUENCY_SIZE); - } - } - - private void writeChildrenPosition(final PtNode ptNode) { - final int childrenPos = BinaryDictEncoderUtils.getChildrenPosition(ptNode); - mPosition += BinaryDictEncoderUtils.writeChildrenPosition(mBuffer, mPosition, - childrenPos); - } - - /** - * Write a shortcut attributes list to mBuffer. - * - * @param shortcuts the shortcut attributes list. - */ - private void writeShortcuts(final ArrayList<WeightedString> shortcuts) { - if (null == shortcuts || shortcuts.isEmpty()) return; - - final int indexOfShortcutByteSize = mPosition; - mPosition += FormatSpec.PTNODE_SHORTCUT_LIST_SIZE_SIZE; - final Iterator<WeightedString> shortcutIterator = shortcuts.iterator(); - while (shortcutIterator.hasNext()) { - final WeightedString target = shortcutIterator.next(); - final int shortcutFlags = BinaryDictEncoderUtils.makeShortcutFlags( - shortcutIterator.hasNext(), - target.getProbability()); - mPosition = BinaryDictEncoderUtils.writeUIntToBuffer(mBuffer, mPosition, shortcutFlags, - FormatSpec.PTNODE_ATTRIBUTE_FLAGS_SIZE); - final int shortcutShift = CharEncoding.writeString(mBuffer, mPosition, target.mWord); - mPosition += shortcutShift; - } - final int shortcutByteSize = mPosition - indexOfShortcutByteSize; - if (shortcutByteSize > FormatSpec.MAX_SHORTCUT_LIST_SIZE_IN_A_PTNODE) { - throw new RuntimeException("Shortcut list too large"); - } - BinaryDictEncoderUtils.writeUIntToBuffer(mBuffer, indexOfShortcutByteSize, shortcutByteSize, - FormatSpec.PTNODE_SHORTCUT_LIST_SIZE_SIZE); - } - - /** - * Write a bigram attributes list to mBuffer. - * - * @param bigrams the bigram attributes list. - * @param dict the dictionary the node array is a part of (for relative offsets). - */ - private void writeBigrams(final ArrayList<WeightedString> bigrams, - final FusionDictionary dict) { - if (bigrams == null) return; - - final Iterator<WeightedString> bigramIterator = bigrams.iterator(); - while (bigramIterator.hasNext()) { - final WeightedString bigram = bigramIterator.next(); - final PtNode target = - FusionDictionary.findWordInTree(dict.mRootNodeArray, bigram.mWord); - final int addressOfBigram = target.mCachedAddressAfterUpdate; - final int unigramFrequencyForThisWord = target.getProbability(); - final int offset = addressOfBigram - - (mPosition + FormatSpec.PTNODE_ATTRIBUTE_FLAGS_SIZE); - final int bigramFlags = BinaryDictEncoderUtils.makeBigramFlags(bigramIterator.hasNext(), - offset, bigram.getProbability(), unigramFrequencyForThisWord, bigram.mWord); - mPosition = BinaryDictEncoderUtils.writeUIntToBuffer(mBuffer, mPosition, bigramFlags, - FormatSpec.PTNODE_ATTRIBUTE_FLAGS_SIZE); - mPosition += BinaryDictEncoderUtils.writeChildrenPosition(mBuffer, mPosition, - Math.abs(offset)); - } - } - - @Override - public void writeForwardLinkAddress(final int forwardLinkAddress) { - mPosition = BinaryDictEncoderUtils.writeUIntToBuffer(mBuffer, mPosition, forwardLinkAddress, - FormatSpec.FORWARD_LINK_ADDRESS_SIZE); - } - - @Override - public void writePtNode(final PtNode ptNode, final FusionDictionary dict) { - writePtNodeFlags(ptNode); - writeCharacters(ptNode.mChars, ptNode.hasSeveralChars()); - writeFrequency(ptNode.getProbability()); - writeChildrenPosition(ptNode); - writeShortcuts(ptNode.mShortcutTargets); - writeBigrams(ptNode.mBigrams, dict); - } -} diff --git a/tests/src/com/android/inputmethod/latin/makedict/Ver4DictDecoder.java b/tests/src/com/android/inputmethod/latin/makedict/Ver4DictDecoder.java index 5e8417ed6..746431dfa 100644 --- a/tests/src/com/android/inputmethod/latin/makedict/Ver4DictDecoder.java +++ b/tests/src/com/android/inputmethod/latin/makedict/Ver4DictDecoder.java @@ -18,7 +18,7 @@ package com.android.inputmethod.latin.makedict; import com.android.inputmethod.annotations.UsedForTesting; import com.android.inputmethod.latin.BinaryDictionary; -import com.android.inputmethod.latin.utils.FileUtils; +import com.android.inputmethod.latin.common.FileUtils; import java.io.File; import java.io.FileNotFoundException; @@ -33,12 +33,7 @@ public class Ver4DictDecoder extends AbstractDictDecoder { final File mDictDirectory; @UsedForTesting - /* package */ Ver4DictDecoder(final File dictDirectory, final int factoryFlag) { - this(dictDirectory, null /* factory */); - } - - @UsedForTesting - /* package */ Ver4DictDecoder(final File dictDirectory, final DictionaryBufferFactory factory) { + /* package */ Ver4DictDecoder(final File dictDirectory) { mDictDirectory = dictDirectory; } @@ -88,21 +83,18 @@ public class Ver4DictDecoder extends AbstractDictDecoder { // Insert unigrams into the fusion dictionary. for (final WordProperty wordProperty : wordProperties) { - if (wordProperty.mIsBlacklistEntry) { - fusionDict.addBlacklistEntry(wordProperty.mWord, wordProperty.mShortcutTargets, - wordProperty.mIsNotAWord); - } else { - fusionDict.add(wordProperty.mWord, wordProperty.mProbabilityInfo, - wordProperty.mShortcutTargets, wordProperty.mIsNotAWord); - } + fusionDict.add(wordProperty.mWord, wordProperty.mProbabilityInfo, + wordProperty.mIsNotAWord, + wordProperty.mIsPossiblyOffensive); } // Insert bigrams into the fusion dictionary. + // TODO: Support ngrams. for (final WordProperty wordProperty : wordProperties) { - if (wordProperty.mBigrams == null) { + if (!wordProperty.mHasNgrams) { continue; } final String word0 = wordProperty.mWord; - for (final WeightedString bigram : wordProperty.mBigrams) { + for (final WeightedString bigram : wordProperty.getBigrams()) { fusionDict.setBigram(word0, bigram.mWord, bigram.mProbabilityInfo); } } diff --git a/tests/src/com/android/inputmethod/latin/makedict/Ver4DictEncoder.java b/tests/src/com/android/inputmethod/latin/makedict/Ver4DictEncoder.java deleted file mode 100644 index 76eaef431..000000000 --- a/tests/src/com/android/inputmethod/latin/makedict/Ver4DictEncoder.java +++ /dev/null @@ -1,150 +0,0 @@ -/* - * 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.annotations.UsedForTesting; -import com.android.inputmethod.latin.BinaryDictionary; -import com.android.inputmethod.latin.Dictionary; -import com.android.inputmethod.latin.PrevWordsInfo; -import com.android.inputmethod.latin.makedict.FormatSpec.FormatOptions; -import com.android.inputmethod.latin.makedict.FusionDictionary.PtNode; -import com.android.inputmethod.latin.utils.BinaryDictionaryUtils; -import com.android.inputmethod.latin.utils.LocaleUtils; - -import java.io.File; -import java.io.IOException; - -/** - * An implementation of DictEncoder for version 4 binary dictionary. - */ -@UsedForTesting -public class Ver4DictEncoder implements DictEncoder { - private final File mDictPlacedDir; - - @UsedForTesting - public Ver4DictEncoder(final File dictPlacedDir) { - mDictPlacedDir = dictPlacedDir; - } - - // TODO: This builds a FusionDictionary first and iterates it to add words to the binary - // dictionary. However, it is possible to just add words directly to the binary dictionary - // instead. - // In the long run, when we stop supporting version 2, FusionDictionary will become deprecated - // and we can remove it. Then we'll be able to just call BinaryDictionary directly. - @Override - public void writeDictionary(FusionDictionary dict, FormatOptions formatOptions) - throws IOException, UnsupportedFormatException { - if (formatOptions.mVersion != FormatSpec.VERSION4) { - throw new UnsupportedFormatException("File header has a wrong version number : " - + formatOptions.mVersion); - } - if (!mDictPlacedDir.isDirectory()) { - throw new UnsupportedFormatException("Given path is not a directory."); - } - if (!BinaryDictionaryUtils.createEmptyDictFile(mDictPlacedDir.getAbsolutePath(), - FormatSpec.VERSION4, LocaleUtils.constructLocaleFromString( - dict.mOptions.mAttributes.get(DictionaryHeader.DICTIONARY_LOCALE_KEY)), - dict.mOptions.mAttributes)) { - throw new IOException("Cannot create dictionary file : " - + mDictPlacedDir.getAbsolutePath()); - } - final BinaryDictionary binaryDict = new BinaryDictionary(mDictPlacedDir.getAbsolutePath(), - 0l, mDictPlacedDir.length(), true /* useFullEditDistance */, - LocaleUtils.constructLocaleFromString(dict.mOptions.mAttributes.get( - DictionaryHeader.DICTIONARY_LOCALE_KEY)), - Dictionary.TYPE_USER /* Dictionary type. Does not matter for us */, - true /* isUpdatable */); - if (!binaryDict.isValidDictionary()) { - // Somehow createEmptyDictFile returned true, but the file was not created correctly - throw new IOException("Cannot create dictionary file"); - } - for (final WordProperty wordProperty : dict) { - // TODO: switch to addMultipleDictionaryEntries when they support shortcuts - if (null == wordProperty.mShortcutTargets || wordProperty.mShortcutTargets.isEmpty()) { - if (!binaryDict.addUnigramEntry(wordProperty.mWord, wordProperty.getProbability(), - null /* shortcutTarget */, 0 /* shortcutProbability */, - wordProperty.mIsBeginningOfSentence, wordProperty.mIsNotAWord, - wordProperty.mIsBlacklistEntry, 0 /* timestamp */)) { - MakedictLog.e("Cannot add unigram entry for " + wordProperty.mWord); - } - } else { - for (final WeightedString shortcutTarget : wordProperty.mShortcutTargets) { - if (!binaryDict.addUnigramEntry(wordProperty.mWord, - wordProperty.getProbability(), - shortcutTarget.mWord, shortcutTarget.getProbability(), - wordProperty.mIsBeginningOfSentence, wordProperty.mIsNotAWord, - wordProperty.mIsBlacklistEntry, 0 /* timestamp */)) { - MakedictLog.e("Cannot add unigram entry for " + wordProperty.mWord - + ", shortcutTarget: " + shortcutTarget.mWord); - return; - } - } - } - if (binaryDict.needsToRunGC(true /* mindsBlockByGC */)) { - if (!binaryDict.flushWithGC()) { - MakedictLog.e("Cannot flush dict with GC."); - return; - } - } - } - for (final WordProperty word0Property : dict) { - if (null == word0Property.mBigrams) continue; - for (final WeightedString word1 : word0Property.mBigrams) { - final PrevWordsInfo prevWordsInfo = - new PrevWordsInfo(new PrevWordsInfo.WordInfo(word0Property.mWord)); - if (!binaryDict.addNgramEntry(prevWordsInfo, word1.mWord, - word1.getProbability(), 0 /* timestamp */)) { - MakedictLog.e("Cannot add n-gram entry for " - + prevWordsInfo + " -> " + word1.mWord); - return; - } - if (binaryDict.needsToRunGC(true /* mindsBlockByGC */)) { - if (!binaryDict.flushWithGC()) { - MakedictLog.e("Cannot flush dict with GC."); - return; - } - } - } - } - if (!binaryDict.flushWithGC()) { - MakedictLog.e("Cannot flush dict with GC."); - return; - } - binaryDict.close(); - } - - @Override - public void setPosition(int position) { - } - - @Override - public int getPosition() { - return 0; - } - - @Override - public void writePtNodeCount(int ptNodeCount) { - } - - @Override - public void writeForwardLinkAddress(int forwardLinkAddress) { - } - - @Override - public void writePtNode(PtNode ptNode, FusionDictionary dict) { - } -} diff --git a/tests/src/com/android/inputmethod/latin/network/BlockingHttpClientTests.java b/tests/src/com/android/inputmethod/latin/network/BlockingHttpClientTests.java new file mode 100644 index 000000000..8f24cdb44 --- /dev/null +++ b/tests/src/com/android/inputmethod/latin/network/BlockingHttpClientTests.java @@ -0,0 +1,168 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.inputmethod.latin.network; + +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.test.AndroidTestCase; +import android.test.suitebuilder.annotation.SmallTest; + +import com.android.inputmethod.latin.network.BlockingHttpClient.ResponseProcessor; + +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; + +import java.io.BufferedInputStream; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.HttpURLConnection; +import java.util.Arrays; +import java.util.Random; + +/** + * Tests for {@link BlockingHttpClient}. + */ +@SmallTest +public class BlockingHttpClientTests extends AndroidTestCase { + @Mock HttpURLConnection mMockHttpConnection; + + @Override + protected void setUp() throws Exception { + super.setUp(); + MockitoAnnotations.initMocks(this); + } + + public void testError_badGateway() throws IOException, AuthException { + when(mMockHttpConnection.getResponseCode()).thenReturn(HttpURLConnection.HTTP_BAD_GATEWAY); + final BlockingHttpClient client = new BlockingHttpClient(mMockHttpConnection); + final FakeErrorResponseProcessor processor = new FakeErrorResponseProcessor(); + + try { + client.execute(null /* empty request */, processor); + fail("Expecting an HttpException"); + } catch (HttpException e) { + // expected HttpException + assertEquals(HttpURLConnection.HTTP_BAD_GATEWAY, e.getHttpStatusCode()); + } + } + + public void testError_clientTimeout() throws Exception { + when(mMockHttpConnection.getResponseCode()).thenReturn( + HttpURLConnection.HTTP_CLIENT_TIMEOUT); + final BlockingHttpClient client = new BlockingHttpClient(mMockHttpConnection); + final FakeErrorResponseProcessor processor = new FakeErrorResponseProcessor(); + + try { + client.execute(null /* empty request */, processor); + fail("Expecting an HttpException"); + } catch (HttpException e) { + // expected HttpException + assertEquals(HttpURLConnection.HTTP_CLIENT_TIMEOUT, e.getHttpStatusCode()); + } + } + + public void testError_forbiddenWithRequest() throws Exception { + final OutputStream mockOutputStream = Mockito.mock(OutputStream.class); + when(mMockHttpConnection.getResponseCode()).thenReturn(HttpURLConnection.HTTP_FORBIDDEN); + when(mMockHttpConnection.getOutputStream()).thenReturn(mockOutputStream); + final BlockingHttpClient client = new BlockingHttpClient(mMockHttpConnection); + final FakeErrorResponseProcessor processor = new FakeErrorResponseProcessor(); + + try { + client.execute(new byte[100], processor); + fail("Expecting an HttpException"); + } catch (HttpException e) { + assertEquals(HttpURLConnection.HTTP_FORBIDDEN, e.getHttpStatusCode()); + } + verify(mockOutputStream).write(any(byte[].class), eq(0), eq(100)); + } + + public void testSuccess_emptyRequest() throws Exception { + final Random rand = new Random(); + byte[] response = new byte[100]; + rand.nextBytes(response); + when(mMockHttpConnection.getResponseCode()).thenReturn(HttpURLConnection.HTTP_OK); + when(mMockHttpConnection.getInputStream()).thenReturn(new ByteArrayInputStream(response)); + final BlockingHttpClient client = new BlockingHttpClient(mMockHttpConnection); + final FakeSuccessResponseProcessor processor = + new FakeSuccessResponseProcessor(response); + + client.execute(null /* empty request */, processor); + assertTrue("ResponseProcessor was not invoked", processor.mInvoked); + } + + public void testSuccess() throws Exception { + final OutputStream mockOutputStream = Mockito.mock(OutputStream.class); + final Random rand = new Random(); + byte[] response = new byte[100]; + rand.nextBytes(response); + when(mMockHttpConnection.getOutputStream()).thenReturn(mockOutputStream); + when(mMockHttpConnection.getResponseCode()).thenReturn(HttpURLConnection.HTTP_OK); + when(mMockHttpConnection.getInputStream()).thenReturn(new ByteArrayInputStream(response)); + final BlockingHttpClient client = new BlockingHttpClient(mMockHttpConnection); + final FakeSuccessResponseProcessor processor = + new FakeSuccessResponseProcessor(response); + + client.execute(new byte[100], processor); + assertTrue("ResponseProcessor was not invoked", processor.mInvoked); + } + + static class FakeErrorResponseProcessor implements ResponseProcessor<Void> { + @Override + public Void onSuccess(InputStream response) { + fail("Expected an error but received success"); + return null; + } + } + + private static class FakeSuccessResponseProcessor implements ResponseProcessor<Void> { + private final byte[] mExpectedResponse; + + boolean mInvoked; + + FakeSuccessResponseProcessor(byte[] expectedResponse) { + mExpectedResponse = expectedResponse; + } + + @Override + public Void onSuccess(InputStream response) { + try { + mInvoked = true; + BufferedInputStream in = new BufferedInputStream(response); + ByteArrayOutputStream buffer = new ByteArrayOutputStream(); + int read = 0; + while ((read = in.read()) != -1) { + buffer.write(read); + } + byte[] actualResponse = buffer.toByteArray(); + in.close(); + assertTrue("Response doesn't match", + Arrays.equals(mExpectedResponse, actualResponse)); + } catch (IOException ex) { + fail("IOException in onSuccess"); + } + return null; + } + } +} diff --git a/tests/src/com/android/inputmethod/latin/network/HttpUrlConnectionBuilderTests.java b/tests/src/com/android/inputmethod/latin/network/HttpUrlConnectionBuilderTests.java new file mode 100644 index 000000000..5b3e78eaf --- /dev/null +++ b/tests/src/com/android/inputmethod/latin/network/HttpUrlConnectionBuilderTests.java @@ -0,0 +1,154 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.inputmethod.latin.network; + +import static com.android.inputmethod.latin.network.HttpUrlConnectionBuilder.MODE_BI_DIRECTIONAL; +import static com.android.inputmethod.latin.network.HttpUrlConnectionBuilder.MODE_DOWNLOAD_ONLY; +import static com.android.inputmethod.latin.network.HttpUrlConnectionBuilder.MODE_UPLOAD_ONLY; + +import android.test.AndroidTestCase; +import android.test.suitebuilder.annotation.SmallTest; + +import java.io.IOException; +import java.net.HttpURLConnection; +import java.net.MalformedURLException; + + +/** + * Tests for {@link HttpUrlConnectionBuilder}. + */ +@SmallTest +public class HttpUrlConnectionBuilderTests extends AndroidTestCase { + + @Override + protected void setUp() throws Exception { + super.setUp(); + } + + public void testSetUrl_malformed() { + HttpUrlConnectionBuilder builder = new HttpUrlConnectionBuilder(); + try { + builder.setUrl("dadasd!@%@!:11"); + fail("Expected a MalformedURLException."); + } catch (MalformedURLException e) { + // Expected + } + } + + public void testSetConnectTimeout_invalid() { + HttpUrlConnectionBuilder builder = new HttpUrlConnectionBuilder(); + try { + builder.setConnectTimeout(-1); + fail("Expected an IllegalArgumentException."); + } catch (IllegalArgumentException e) { + // Expected + } + } + + public void testSetConnectTimeout() throws IOException { + HttpUrlConnectionBuilder builder = new HttpUrlConnectionBuilder(); + builder.setUrl("https://www.example.com"); + builder.setConnectTimeout(8765); + HttpURLConnection connection = builder.build(); + assertEquals(8765, connection.getConnectTimeout()); + } + + public void testSetReadTimeout_invalid() { + HttpUrlConnectionBuilder builder = new HttpUrlConnectionBuilder(); + try { + builder.setReadTimeout(-1); + fail("Expected an IllegalArgumentException."); + } catch (IllegalArgumentException e) { + // Expected + } + } + + public void testSetReadTimeout() throws IOException { + HttpUrlConnectionBuilder builder = new HttpUrlConnectionBuilder(); + builder.setUrl("https://www.example.com"); + builder.setReadTimeout(8765); + HttpURLConnection connection = builder.build(); + assertEquals(8765, connection.getReadTimeout()); + } + + public void testAddHeader() throws IOException { + HttpUrlConnectionBuilder builder = new HttpUrlConnectionBuilder(); + builder.setUrl("http://www.example.com"); + builder.addHeader("some-random-key", "some-random-value"); + HttpURLConnection connection = builder.build(); + assertEquals("some-random-value", connection.getRequestProperty("some-random-key")); + } + + public void testSetUseCache_notSet() throws IOException { + HttpUrlConnectionBuilder builder = new HttpUrlConnectionBuilder(); + builder.setUrl("http://www.example.com"); + HttpURLConnection connection = builder.build(); + assertFalse(connection.getUseCaches()); + } + + public void testSetUseCache_false() throws IOException { + HttpUrlConnectionBuilder builder = new HttpUrlConnectionBuilder(); + builder.setUrl("http://www.example.com"); + HttpURLConnection connection = builder.build(); + connection.setUseCaches(false); + assertFalse(connection.getUseCaches()); + } + + public void testSetUseCache_true() throws IOException { + HttpUrlConnectionBuilder builder = new HttpUrlConnectionBuilder(); + builder.setUrl("http://www.example.com"); + HttpURLConnection connection = builder.build(); + connection.setUseCaches(true); + assertTrue(connection.getUseCaches()); + } + + public void testSetMode_uploadOnly() throws IOException { + HttpUrlConnectionBuilder builder = new HttpUrlConnectionBuilder(); + builder.setUrl("http://www.example.com"); + builder.setMode(MODE_UPLOAD_ONLY); + HttpURLConnection connection = builder.build(); + assertTrue(connection.getDoInput()); + assertFalse(connection.getDoOutput()); + } + + public void testSetMode_downloadOnly() throws IOException { + HttpUrlConnectionBuilder builder = new HttpUrlConnectionBuilder(); + builder.setUrl("https://www.example.com"); + builder.setMode(MODE_DOWNLOAD_ONLY); + HttpURLConnection connection = builder.build(); + assertFalse(connection.getDoInput()); + assertTrue(connection.getDoOutput()); + } + + public void testSetMode_bidirectional() throws IOException { + HttpUrlConnectionBuilder builder = new HttpUrlConnectionBuilder(); + builder.setUrl("https://www.example.com"); + builder.setMode(MODE_BI_DIRECTIONAL); + HttpURLConnection connection = builder.build(); + assertTrue(connection.getDoInput()); + assertTrue(connection.getDoOutput()); + } + + public void testSetAuthToken() throws IOException { + HttpUrlConnectionBuilder builder = new HttpUrlConnectionBuilder(); + builder.setUrl("https://www.example.com"); + builder.setAuthToken("some-random-auth-token"); + HttpURLConnection connection = builder.build(); + assertEquals("some-random-auth-token", + connection.getRequestProperty(HttpUrlConnectionBuilder.HTTP_HEADER_AUTHORIZATION)); + } +} diff --git a/tests/src/com/android/inputmethod/latin/personalization/ContextualDictionaryTests.java b/tests/src/com/android/inputmethod/latin/personalization/ContextualDictionaryTests.java deleted file mode 100644 index 565fadb2a..000000000 --- a/tests/src/com/android/inputmethod/latin/personalization/ContextualDictionaryTests.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.inputmethod.latin.personalization; - -import java.io.File; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Locale; -import java.util.Map; - -import com.android.inputmethod.latin.Dictionary; -import com.android.inputmethod.latin.DictionaryFacilitator; -import com.android.inputmethod.latin.ExpandableBinaryDictionary; - -import android.test.AndroidTestCase; -import android.test.suitebuilder.annotation.LargeTest; - -/** - * Unit tests for contextual dictionary - */ -@LargeTest -public class ContextualDictionaryTests extends AndroidTestCase { - private static final String TAG = ContextualDictionaryTests.class.getSimpleName(); - - private static final Locale LOCALE_EN_US = new Locale("en", "US"); - - private DictionaryFacilitator getDictionaryFacilitator() { - final ArrayList<String> dictTypes = new ArrayList<>(); - dictTypes.add(Dictionary.TYPE_CONTEXTUAL); - final DictionaryFacilitator dictionaryFacilitator = new DictionaryFacilitator(); - dictionaryFacilitator.resetDictionariesForTesting(getContext(), LOCALE_EN_US, dictTypes, - new HashMap<String, File>(), new HashMap<String, Map<String, String>>()); - return dictionaryFacilitator; - } - - public void testAddPhrase() { - final DictionaryFacilitator dictionaryFacilitator = getDictionaryFacilitator(); - final String[] phrase = new String[] {"a", "b", "c", "d"}; - final int probability = 100; - final int bigramProbabilityForWords = 150; - final int bigramProbabilityForPhrases = 200; - dictionaryFacilitator.addPhraseToContextualDictionary( - phrase, probability, bigramProbabilityForWords, bigramProbabilityForPhrases); - final ExpandableBinaryDictionary contextualDictionary = - dictionaryFacilitator.getSubDictForTesting(Dictionary.TYPE_CONTEXTUAL); - contextualDictionary.waitAllTasksForTests(); - // Word - assertTrue(contextualDictionary.isInDictionary("a")); - assertTrue(contextualDictionary.isInDictionary("b")); - assertTrue(contextualDictionary.isInDictionary("c")); - assertTrue(contextualDictionary.isInDictionary("d")); - // Phrase - assertTrue(contextualDictionary.isInDictionary("a b c d")); - assertTrue(contextualDictionary.isInDictionary("b c d")); - assertTrue(contextualDictionary.isInDictionary("c d")); - assertFalse(contextualDictionary.isInDictionary("a b c")); - assertFalse(contextualDictionary.isInDictionary("abcd")); - // TODO: Add tests for probability. - // TODO: Add tests for n-grams. - } -} diff --git a/tests/src/com/android/inputmethod/latin/personalization/PersonalizationDictionaryTests.java b/tests/src/com/android/inputmethod/latin/personalization/PersonalizationDictionaryTests.java deleted file mode 100644 index 0f2f9814b..000000000 --- a/tests/src/com/android/inputmethod/latin/personalization/PersonalizationDictionaryTests.java +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.inputmethod.latin.personalization; - -import java.io.File; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Locale; -import java.util.Map; -import java.util.Random; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; - -import com.android.inputmethod.latin.BinaryDictionary; -import com.android.inputmethod.latin.Dictionary; -import com.android.inputmethod.latin.DictionaryFacilitator; -import com.android.inputmethod.latin.ExpandableBinaryDictionary; -import com.android.inputmethod.latin.ExpandableBinaryDictionary.AddMultipleDictionaryEntriesCallback; -import com.android.inputmethod.latin.makedict.CodePointUtils; -import com.android.inputmethod.latin.settings.SpacingAndPunctuations; - -import android.test.AndroidTestCase; -import android.test.suitebuilder.annotation.LargeTest; -import android.util.Log; - -/** - * Unit tests for personalization dictionary - */ -@LargeTest -public class PersonalizationDictionaryTests extends AndroidTestCase { - private static final String TAG = PersonalizationDictionaryTests.class.getSimpleName(); - - private static final Locale LOCALE_EN_US = new Locale("en", "US"); - private static final String DUMMY_PACKAGE_NAME = "test.package.name"; - private static final long TIMEOUT_TO_WAIT_DICTIONARY_OPERATIONS_IN_SECONDS = 120; - - private DictionaryFacilitator getDictionaryFacilitator() { - final ArrayList<String> dictTypes = new ArrayList<>(); - dictTypes.add(Dictionary.TYPE_MAIN); - dictTypes.add(Dictionary.TYPE_PERSONALIZATION); - final DictionaryFacilitator dictionaryFacilitator = new DictionaryFacilitator(); - dictionaryFacilitator.resetDictionariesForTesting(getContext(), LOCALE_EN_US, dictTypes, - new HashMap<String, File>(), new HashMap<String, Map<String, String>>()); - return dictionaryFacilitator; - } - - public void testAddManyTokens() { - final DictionaryFacilitator dictionaryFacilitator = getDictionaryFacilitator(); - dictionaryFacilitator.clearPersonalizationDictionary(); - final int dataChunkCount = 20; - final int wordCountInOneChunk = 2000; - final Random random = new Random(System.currentTimeMillis()); - final int[] codePointSet = CodePointUtils.LATIN_ALPHABETS_LOWER; - - final SpacingAndPunctuations spacingAndPunctuations = - new SpacingAndPunctuations(getContext().getResources()); - - final int timeStampInSeconds = (int)TimeUnit.MILLISECONDS.toSeconds( - System.currentTimeMillis()); - - for (int i = 0; i < dataChunkCount; i++) { - final ArrayList<String> tokens = new ArrayList<>(); - for (int j = 0; j < wordCountInOneChunk; j++) { - tokens.add(CodePointUtils.generateWord(random, codePointSet)); - } - final PersonalizationDataChunk personalizationDataChunk = new PersonalizationDataChunk( - true /* inputByUser */, tokens, timeStampInSeconds, DUMMY_PACKAGE_NAME); - final CountDownLatch countDownLatch = new CountDownLatch(1); - final AddMultipleDictionaryEntriesCallback callback = - new AddMultipleDictionaryEntriesCallback() { - @Override - public void onFinished() { - countDownLatch.countDown(); - } - }; - dictionaryFacilitator.addEntriesToPersonalizationDictionary(personalizationDataChunk, - spacingAndPunctuations, callback); - try { - countDownLatch.await(TIMEOUT_TO_WAIT_DICTIONARY_OPERATIONS_IN_SECONDS, - TimeUnit.SECONDS); - } catch (InterruptedException e) { - Log.e(TAG, "Interrupted while waiting for finishing dictionary operations.", e); - } - } - dictionaryFacilitator.flushPersonalizationDictionary(); - try { - dictionaryFacilitator.waitForLoadingDictionariesForTesting( - TIMEOUT_TO_WAIT_DICTIONARY_OPERATIONS_IN_SECONDS, TimeUnit.SECONDS); - } catch (InterruptedException e) { - Log.e(TAG, "Interrupted while waiting for finishing dictionary operations.", e); - } - final String dictName = ExpandableBinaryDictionary.getDictName( - PersonalizationDictionary.NAME, LOCALE_EN_US, null /* dictFile */); - final File dictFile = ExpandableBinaryDictionary.getDictFile( - getContext(), dictName, null /* dictFile */); - - final BinaryDictionary binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(), - 0 /* offset */, 0 /* size */, - true /* useFullEditDistance */, LOCALE_EN_US, Dictionary.TYPE_PERSONALIZATION, - true /* isUpdatable */); - assertTrue(binaryDictionary.isValidDictionary()); - } -} diff --git a/tests/src/com/android/inputmethod/latin/personalization/UserHistoryDictionaryTests.java b/tests/src/com/android/inputmethod/latin/personalization/UserHistoryDictionaryTests.java index f87f3b494..559f28642 100644 --- a/tests/src/com/android/inputmethod/latin/personalization/UserHistoryDictionaryTests.java +++ b/tests/src/com/android/inputmethod/latin/personalization/UserHistoryDictionaryTests.java @@ -21,19 +21,11 @@ import android.test.suitebuilder.annotation.LargeTest; import android.util.Log; import com.android.inputmethod.latin.ExpandableBinaryDictionary; -import com.android.inputmethod.latin.PrevWordsInfo; -import com.android.inputmethod.latin.PrevWordsInfo.WordInfo; import com.android.inputmethod.latin.utils.BinaryDictionaryUtils; -import com.android.inputmethod.latin.utils.DistracterFilter; -import com.android.inputmethod.latin.utils.FileUtils; import java.io.File; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; import java.util.Locale; import java.util.Random; -import java.util.concurrent.TimeUnit; /** * Unit tests for UserHistoryDictionary @@ -41,22 +33,47 @@ import java.util.concurrent.TimeUnit; @LargeTest public class UserHistoryDictionaryTests extends AndroidTestCase { private static final String TAG = UserHistoryDictionaryTests.class.getSimpleName(); - - 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 WAIT_FOR_WRITING_FILE_IN_MILLISECONDS = 3000; + private static final String TEST_ACCOUNT = "account@example.com"; private int mCurrentTime = 0; + private static void printAllFiles(final File dir) { + Log.d(TAG, dir.getAbsolutePath()); + for (final File file : dir.listFiles()) { + Log.d(TAG, " " + file.getName()); + } + } + + private static void assertDictionaryExists(final UserHistoryDictionary dict, + final File dictFile) { + Log.d(TAG, "waiting for writing ..."); + dict.waitAllTasksForTests(); + if (!dictFile.exists()) { + try { + Log.d(TAG, dictFile + " is not existing. Wait " + + WAIT_FOR_WRITING_FILE_IN_MILLISECONDS + " ms for writing."); + printAllFiles(dictFile.getParentFile()); + Thread.sleep(WAIT_FOR_WRITING_FILE_IN_MILLISECONDS); + } catch (final InterruptedException e) { + Log.e(TAG, "Interrupted during waiting for writing the dict file."); + } + } + assertTrue("Following dictionary file doesn't exist: " + dictFile, dictFile.exists()); + } + @Override protected void setUp() throws Exception { super.setUp(); resetCurrentTimeForTestMode(); + UserHistoryDictionaryTestsHelper.removeAllTestDictFiles( + UserHistoryDictionaryTestsHelper.TEST_LOCALE_PREFIX, mContext); } @Override protected void tearDown() throws Exception { + UserHistoryDictionaryTestsHelper.removeAllTestDictFiles( + UserHistoryDictionaryTestsHelper.TEST_LOCALE_PREFIX, mContext); stopTestModeInNativeCode(); super.tearDown(); } @@ -66,20 +83,6 @@ public class UserHistoryDictionaryTests extends AndroidTestCase { setCurrentTimeForTestMode(mCurrentTime); } - private void forcePassingShortTime() { - // 3 days. - final int timeToElapse = (int)TimeUnit.DAYS.toSeconds(3); - mCurrentTime += timeToElapse; - setCurrentTimeForTestMode(mCurrentTime); - } - - private void forcePassingLongTime() { - // 365 days. - final int timeToElapse = (int)TimeUnit.DAYS.toSeconds(365); - mCurrentTime += timeToElapse; - setCurrentTimeForTestMode(mCurrentTime); - } - private static int setCurrentTimeForTestMode(final int currentTime) { return BinaryDictionaryUtils.setCurrentTimeForTest(currentTime); } @@ -89,199 +92,122 @@ public class UserHistoryDictionaryTests extends AndroidTestCase { } /** - * Generates a random word. - */ - private static 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 static List<String> generateWords(final int number, final Random random) { - final HashSet<String> wordSet = new HashSet<>(); - while (wordSet.size() < number) { - wordSet.add(generateWord(random.nextInt())); - } - return new ArrayList<>(wordSet); - } - - private static void addToDict(final UserHistoryDictionary dict, final List<String> words) { - PrevWordsInfo prevWordsInfo = PrevWordsInfo.EMPTY_PREV_WORDS_INFO; - for (String word : words) { - UserHistoryDictionary.addToDictionary(dict, prevWordsInfo, word, true, - (int)TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis()), - DistracterFilter.EMPTY_DISTRACTER_FILTER); - prevWordsInfo = prevWordsInfo.getNextPrevWordsInfo(new WordInfo(word)); - } - } - - /** - * @param checkContents if true, checks whether written words are actually in the dictionary - * or not. - */ - private void addAndWriteRandomWords(final Locale locale, final int numberOfWords, - final Random random, final boolean checkContents) { - final List<String> words = generateWords(numberOfWords, random); - final UserHistoryDictionary dict = PersonalizationHelper.getUserHistoryDictionary( - mContext, locale); - // Add random words to the user history dictionary. - addToDict(dict, words); - if (checkContents) { - dict.waitAllTasksForTests(); - for (int i = 0; i < numberOfWords; ++i) { - final String word = words.get(i); - assertTrue(dict.isInDictionary(word)); - } - } - // write to file. - dict.close(); - } - - /** * Clear all entries in the user history dictionary. - * @param locale dummy locale for testing. + * @param dict the user history dictionary. */ - private void clearHistory(final Locale locale) { - final UserHistoryDictionary dict = PersonalizationHelper.getUserHistoryDictionary( - mContext, locale); + private static void clearHistory(final UserHistoryDictionary dict) { dict.waitAllTasksForTests(); dict.clear(); dict.close(); dict.waitAllTasksForTests(); } - /** - * Shut down executer and wait until all operations of user history are done. - * @param locale dummy locale for testing. - */ - private void waitForWriting(final Locale locale) { - final UserHistoryDictionary dict = PersonalizationHelper.getUserHistoryDictionary( - mContext, locale); - dict.waitAllTasksForTests(); - } - - public void testRandomWords() { + private void doTestRandomWords(final String testAccount) { Log.d(TAG, "This test can be used for profiling."); Log.d(TAG, "Usage: please set UserHistoryDictionary.PROFILE_SAVE_RESTORE to true."); - final Locale dummyLocale = new Locale("test_random_words" + System.currentTimeMillis()); - final String dictName = ExpandableBinaryDictionary.getDictName( - UserHistoryDictionary.NAME, dummyLocale, null /* dictFile */); + final Locale dummyLocale = UserHistoryDictionaryTestsHelper.getDummyLocale("random_words"); + final String dictName = UserHistoryDictionary.getUserHistoryDictName( + UserHistoryDictionary.NAME, dummyLocale, + null /* dictFile */, + testAccount /* account */); final File dictFile = ExpandableBinaryDictionary.getDictFile( mContext, dictName, null /* dictFile */); + final UserHistoryDictionary dict = PersonalizationHelper.getUserHistoryDictionary( + getContext(), dummyLocale, testAccount); + clearHistory(dict); final int numberOfWords = 1000; final Random random = new Random(123456); + assertTrue(UserHistoryDictionaryTestsHelper.addAndWriteRandomWords( + dict, numberOfWords, random, true /* checksContents */, mCurrentTime)); + assertDictionaryExists(dict, dictFile); + } - try { - clearHistory(dummyLocale); - addAndWriteRandomWords(dummyLocale, numberOfWords, random, - true /* checksContents */); - } finally { - Log.d(TAG, "waiting for writing ..."); - waitForWriting(dummyLocale); - assertTrue("check exisiting of " + dictFile, dictFile.exists()); - FileUtils.deleteRecursively(dictFile); - } + public void testRandomWords_NullAccount() { + doTestRandomWords(null /* testAccount */); + } + + public void testRandomWords() { + doTestRandomWords(TEST_ACCOUNT); } public void testStressTestForSwitchingLanguagesAndAddingWords() { + doTestStressTestForSwitchingLanguagesAndAddingWords(TEST_ACCOUNT); + } + + public void testStressTestForSwitchingLanguagesAndAddingWords_NullAccount() { + doTestStressTestForSwitchingLanguagesAndAddingWords(null /* testAccount */); + } + + private void doTestStressTestForSwitchingLanguagesAndAddingWords(final String testAccount) { final int numberOfLanguages = 2; final int numberOfLanguageSwitching = 80; final int numberOfWordsInsertedForEachLanguageSwitch = 100; final File dictFiles[] = new File[numberOfLanguages]; - final Locale dummyLocales[] = new Locale[numberOfLanguages]; + final UserHistoryDictionary dicts[] = new UserHistoryDictionary[numberOfLanguages]; + try { final Random random = new Random(123456); // Create filename suffixes for this test. for (int i = 0; i < numberOfLanguages; i++) { - dummyLocales[i] = new Locale("test_switching_languages" + i); - final String dictName = ExpandableBinaryDictionary.getDictName( - UserHistoryDictionary.NAME, dummyLocales[i], null /* dictFile */); + final Locale dummyLocale = + UserHistoryDictionaryTestsHelper.getDummyLocale("switching_languages" + i); + final String dictName = UserHistoryDictionary.getUserHistoryDictName( + UserHistoryDictionary.NAME, dummyLocale, null /* dictFile */, + testAccount /* account */); dictFiles[i] = ExpandableBinaryDictionary.getDictFile( mContext, dictName, null /* dictFile */); - clearHistory(dummyLocales[i]); + dicts[i] = PersonalizationHelper.getUserHistoryDictionary(getContext(), + dummyLocale, testAccount); + clearHistory(dicts[i]); } final long start = System.currentTimeMillis(); for (int i = 0; i < numberOfLanguageSwitching; i++) { final int index = i % numberOfLanguages; - // Switch languages to testFilenameSuffixes[index]. - addAndWriteRandomWords(dummyLocales[index], - numberOfWordsInsertedForEachLanguageSwitch, random, - false /* checksContents */); + // Switch to dicts[index]. + assertTrue(UserHistoryDictionaryTestsHelper.addAndWriteRandomWords(dicts[index], + numberOfWordsInsertedForEachLanguageSwitch, + random, + false /* checksContents */, + mCurrentTime)); } final long end = System.currentTimeMillis(); Log.d(TAG, "testStressTestForSwitchingLanguageAndAddingWords took " + (end - start) + " ms"); } finally { - Log.d(TAG, "waiting for writing ..."); for (int i = 0; i < numberOfLanguages; i++) { - waitForWriting(dummyLocales[i]); - } - for (final File dictFile : dictFiles) { - assertTrue("check exisiting of " + dictFile, dictFile.exists()); - FileUtils.deleteRecursively(dictFile); + assertDictionaryExists(dicts[i], dictFiles[i]); } } } public void testAddManyWords() { - final Locale dummyLocale = new Locale("test_random_words" + System.currentTimeMillis()); - final String dictName = ExpandableBinaryDictionary.getDictName( - UserHistoryDictionary.NAME, dummyLocale, null /* dictFile */); + doTestAddManyWords(TEST_ACCOUNT); + } + + public void testAddManyWords_NullAccount() { + doTestAddManyWords(null /* testAccount */); + } + + private void doTestAddManyWords(final String testAccount) { + final Locale dummyLocale = + UserHistoryDictionaryTestsHelper.getDummyLocale("many_random_words"); + final String dictName = UserHistoryDictionary.getUserHistoryDictName( + UserHistoryDictionary.NAME, dummyLocale, null /* dictFile */, testAccount); final File dictFile = ExpandableBinaryDictionary.getDictFile( mContext, dictName, null /* dictFile */); final int numberOfWords = 10000; final Random random = new Random(123456); - clearHistory(dummyLocale); - try { - addAndWriteRandomWords(dummyLocale, numberOfWords, random, true /* checksContents */); - } finally { - Log.d(TAG, "waiting for writing ..."); - waitForWriting(dummyLocale); - assertTrue("check exisiting of " + dictFile, dictFile.exists()); - FileUtils.deleteRecursively(dictFile); - } - } - - public void testDecaying() { - final Locale dummyLocale = new Locale("test_decaying" + System.currentTimeMillis()); - final int numberOfWords = 5000; - final Random random = new Random(123456); - resetCurrentTimeForTestMode(); - clearHistory(dummyLocale); - final List<String> words = generateWords(numberOfWords, random); - final UserHistoryDictionary dict = - PersonalizationHelper.getUserHistoryDictionary(getContext(), dummyLocale); - dict.waitAllTasksForTests(); - PrevWordsInfo prevWordsInfo = PrevWordsInfo.EMPTY_PREV_WORDS_INFO; - for (final String word : words) { - UserHistoryDictionary.addToDictionary(dict, prevWordsInfo, word, true, mCurrentTime, - DistracterFilter.EMPTY_DISTRACTER_FILTER); - prevWordsInfo = prevWordsInfo.getNextPrevWordsInfo(new WordInfo(word)); - dict.waitAllTasksForTests(); - assertTrue(dict.isInDictionary(word)); - } - forcePassingShortTime(); - dict.runGCIfRequired(); - dict.waitAllTasksForTests(); - for (final String word : words) { - assertTrue(dict.isInDictionary(word)); - } - forcePassingLongTime(); - dict.runGCIfRequired(); - dict.waitAllTasksForTests(); - for (final String word : words) { - assertFalse(dict.isInDictionary(word)); - } + final UserHistoryDictionary dict = PersonalizationHelper.getUserHistoryDictionary( + getContext(), dummyLocale, testAccount); + clearHistory(dict); + assertTrue(UserHistoryDictionaryTestsHelper.addAndWriteRandomWords(dict, + numberOfWords, random, true /* checksContents */, mCurrentTime)); + assertDictionaryExists(dict, dictFile); } -} +}
\ No newline at end of file diff --git a/tests/src/com/android/inputmethod/latin/personalization/UserHistoryDictionaryTestsHelper.java b/tests/src/com/android/inputmethod/latin/personalization/UserHistoryDictionaryTestsHelper.java new file mode 100644 index 000000000..4b7b9bc36 --- /dev/null +++ b/tests/src/com/android/inputmethod/latin/personalization/UserHistoryDictionaryTestsHelper.java @@ -0,0 +1,144 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.inputmethod.latin.personalization; + +import android.content.Context; + +import com.android.inputmethod.latin.BinaryDictionary; +import com.android.inputmethod.latin.NgramContext; +import com.android.inputmethod.latin.NgramContext.WordInfo; +import com.android.inputmethod.latin.common.FileUtils; + +import java.io.File; +import java.io.FilenameFilter; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Locale; +import java.util.Random; + +/** + * Utility class for helping while running tests involving {@link UserHistoryDictionary}. + */ +public class UserHistoryDictionaryTestsHelper { + + /** + * Locale prefix for generating dummy locales for tests. + */ + public static final String TEST_LOCALE_PREFIX = "test-"; + + /** + * Characters for generating random words. + */ + 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" + }; + + /** + * Remove all the test dictionary files created for the given locale. + */ + public static void removeAllTestDictFiles(final String filter, final Context context) { + final FilenameFilter filenameFilter = new FilenameFilter() { + @Override + public boolean accept(final File dir, final String filename) { + return filename.startsWith(UserHistoryDictionary.NAME + "." + filter); + } + }; + FileUtils.deleteFilteredFiles(context.getFilesDir(), filenameFilter); + } + + /** + * Generates and writes random words to dictionary. Caller can be assured + * that the write tasks would be finished; and its success would be reflected + * in the returned boolean. + * + * @param dict {@link UserHistoryDictionary} to which words should be added. + * @param numberOfWords number of words to be added. + * @param random helps generate random words. + * @param checkContents if true, checks whether written words are actually in the dictionary. + * @param currentTime timestamp that would be used for adding the words. + * @returns true if all words have been written to dictionary successfully. + */ + public static boolean addAndWriteRandomWords(final UserHistoryDictionary dict, + final int numberOfWords, final Random random, final boolean checkContents, + final int currentTime) { + final List<String> words = generateWords(numberOfWords, random); + // Add random words to the user history dictionary. + addWordsToDictionary(dict, words, currentTime); + boolean success = true; + if (checkContents) { + dict.waitAllTasksForTests(); + for (int i = 0; i < numberOfWords; ++i) { + final String word = words.get(i); + if (!dict.isInDictionary(word)) { + success = false; + break; + } + } + } + // write to file. + dict.close(); + dict.waitAllTasksForTests(); + return success; + } + + private static void addWordsToDictionary(final UserHistoryDictionary dict, + final List<String> words, final int timestamp) { + NgramContext ngramContext = NgramContext.getEmptyPrevWordsContext( + BinaryDictionary.MAX_PREV_WORD_COUNT_FOR_N_GRAM); + for (final String word : words) { + UserHistoryDictionary.addToDictionary(dict, ngramContext, word, true, timestamp); + ngramContext = ngramContext.getNextNgramContext(new WordInfo(word)); + } + } + + /** + * Creates unique test locale for using within tests. + */ + public static Locale getDummyLocale(final String name) { + return new Locale(TEST_LOCALE_PREFIX + name + System.currentTimeMillis()); + } + + /** + * Generates random words. + * + * @param numberOfWords number of words to generate. + * @param random salt used for generating random words. + */ + public static List<String> generateWords(final int numberOfWords, final Random random) { + final HashSet<String> wordSet = new HashSet<>(); + while (wordSet.size() < numberOfWords) { + wordSet.add(generateWord(random.nextInt())); + } + return new ArrayList<>(wordSet); + } + + /** + * Generates a random word. + */ + private static String generateWord(final int value) { + final int lengthOfChars = CHARACTERS.length; + final StringBuilder builder = new StringBuilder(); + long lvalue = Math.abs((long)value); + while (lvalue > 0) { + builder.append(CHARACTERS[(int)(lvalue % lengthOfChars)]); + lvalue /= lengthOfChars; + } + return builder.toString(); + } +} diff --git a/tests/src/com/android/inputmethod/latin/settings/AccountsSettingsFragmentTests.java b/tests/src/com/android/inputmethod/latin/settings/AccountsSettingsFragmentTests.java new file mode 100644 index 000000000..7a019c364 --- /dev/null +++ b/tests/src/com/android/inputmethod/latin/settings/AccountsSettingsFragmentTests.java @@ -0,0 +1,171 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.inputmethod.latin.settings; + +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import android.app.AlertDialog; +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.test.ActivityInstrumentationTestCase2; +import android.test.suitebuilder.annotation.MediumTest; +import android.view.View; +import android.widget.ListView; + +import com.android.inputmethod.latin.utils.ManagedProfileUtils; + +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +@MediumTest +public class AccountsSettingsFragmentTests + extends ActivityInstrumentationTestCase2<TestFragmentActivity> { + private static final String FRAG_NAME = AccountsSettingsFragment.class.getName(); + private static final long TEST_TIMEOUT_MILLIS = 5000; + + @Mock private ManagedProfileUtils mManagedProfileUtils; + + public AccountsSettingsFragmentTests() { + super(TestFragmentActivity.class); + } + + @Override + protected void setUp() throws Exception { + super.setUp(); + + // Initialize the mocks. + MockitoAnnotations.initMocks(this); + ManagedProfileUtils.setTestInstance(mManagedProfileUtils); + + Intent intent = new Intent(); + intent.putExtra(TestFragmentActivity.EXTRA_SHOW_FRAGMENT, FRAG_NAME); + setActivityIntent(intent); + } + + @Override + public void tearDown() throws Exception { + ManagedProfileUtils.setTestInstance(null); + super.tearDown(); + } + + public void testEmptyAccounts() { + final AccountsSettingsFragment fragment = + (AccountsSettingsFragment) getActivity().mFragment; + try { + fragment.createAccountPicker(new String[0], null, null /* listener */); + fail("Expected IllegalArgumentException, never thrown"); + } catch (IllegalArgumentException expected) { + // Expected. + } + } + + private static class DialogHolder { + AlertDialog mDialog; + DialogHolder() {} + } + + public void testMultipleAccounts_noSettingsForManagedProfile() { + when(mManagedProfileUtils.hasWorkProfile(any(Context.class))).thenReturn(true); + + final AccountsSettingsFragment fragment = + (AccountsSettingsFragment) getActivity().mFragment; + final AlertDialog dialog = initDialog(fragment, null).mDialog; + final ListView lv = dialog.getListView(); + + // Nothing to check/uncheck. + assertNull(fragment.findPreference(AccountsSettingsFragment.PREF_ACCCOUNT_SWITCHER)); + } + + public void testMultipleAccounts_noCurrentAccount() { + when(mManagedProfileUtils.hasWorkProfile(any(Context.class))).thenReturn(false); + + final AccountsSettingsFragment fragment = + (AccountsSettingsFragment) getActivity().mFragment; + final AlertDialog dialog = initDialog(fragment, null).mDialog; + final ListView lv = dialog.getListView(); + + // The 1st account should be checked by default. + assertEquals("checked-item", 0, lv.getCheckedItemPosition()); + // There should be 4 accounts in the list. + assertEquals("count", 4, lv.getCount()); + // The sign-out button shouldn't exist + assertEquals(View.GONE, + dialog.getButton(DialogInterface.BUTTON_NEUTRAL).getVisibility()); + assertEquals(View.VISIBLE, + dialog.getButton(DialogInterface.BUTTON_NEGATIVE).getVisibility()); + assertEquals(View.VISIBLE, + dialog.getButton(DialogInterface.BUTTON_POSITIVE).getVisibility()); + } + + public void testMultipleAccounts_currentAccount() { + when(mManagedProfileUtils.hasWorkProfile(any(Context.class))).thenReturn(false); + + final AccountsSettingsFragment fragment = + (AccountsSettingsFragment) getActivity().mFragment; + final AlertDialog dialog = initDialog(fragment, "3@example.com").mDialog; + final ListView lv = dialog.getListView(); + + // The 3rd account should be checked by default. + assertEquals("checked-item", 2, lv.getCheckedItemPosition()); + // There should be 4 accounts in the list. + assertEquals("count", 4, lv.getCount()); + // The sign-out button should be shown + assertEquals(View.VISIBLE, + dialog.getButton(DialogInterface.BUTTON_NEUTRAL).getVisibility()); + assertEquals(View.VISIBLE, + dialog.getButton(DialogInterface.BUTTON_NEGATIVE).getVisibility()); + assertEquals(View.VISIBLE, + dialog.getButton(DialogInterface.BUTTON_POSITIVE).getVisibility()); + } + + private DialogHolder initDialog( + final AccountsSettingsFragment fragment, + final String selectedAccount) { + final DialogHolder dialogHolder = new DialogHolder(); + final CountDownLatch latch = new CountDownLatch(1); + + getActivity().runOnUiThread(new Runnable() { + @Override + public void run() { + final AlertDialog dialog = fragment.createAccountPicker( + new String[] { + "1@example.com", + "2@example.com", + "3@example.com", + "4@example.com"}, + selectedAccount, null /* positiveButtonListner */); + dialog.show(); + dialogHolder.mDialog = dialog; + latch.countDown(); + } + }); + + try { + latch.await(TEST_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS); + } catch (InterruptedException ex) { + fail(); + } + getInstrumentation().waitForIdleSync(); + return dialogHolder; + } +} diff --git a/tests/src/com/android/inputmethod/latin/settings/SpacingAndPunctuationsTests.java b/tests/src/com/android/inputmethod/latin/settings/SpacingAndPunctuationsTests.java index eb76032b1..ed632db68 100644 --- a/tests/src/com/android/inputmethod/latin/settings/SpacingAndPunctuationsTests.java +++ b/tests/src/com/android/inputmethod/latin/settings/SpacingAndPunctuationsTests.java @@ -20,9 +20,8 @@ import android.content.res.Resources; import android.test.AndroidTestCase; import android.test.suitebuilder.annotation.SmallTest; -import com.android.inputmethod.latin.Constants; -import com.android.inputmethod.latin.R; import com.android.inputmethod.latin.SuggestedWords; +import com.android.inputmethod.latin.common.Constants; import com.android.inputmethod.latin.utils.RunInLocale; import junit.framework.AssertionFailedError; @@ -37,13 +36,11 @@ public class SpacingAndPunctuationsTests extends AndroidTestCase { private int mScreenMetrics; private boolean isPhone() { - return mScreenMetrics == Constants.SCREEN_METRICS_SMALL_PHONE - || mScreenMetrics == Constants.SCREEN_METRICS_LARGE_PHONE; + return Constants.isPhone(mScreenMetrics); } private boolean isTablet() { - return mScreenMetrics == Constants.SCREEN_METRICS_SMALL_TABLET - || mScreenMetrics == Constants.SCREEN_METRICS_LARGE_TABLET; + return Constants.isTablet(mScreenMetrics); } private SpacingAndPunctuations ENGLISH; @@ -70,7 +67,7 @@ public class SpacingAndPunctuationsTests extends AndroidTestCase { protected void setUp() throws Exception { super.setUp(); - mScreenMetrics = mContext.getResources().getInteger(R.integer.config_screen_metrics); + mScreenMetrics = Settings.readScreenMetrics(getContext().getResources()); // Language only ENGLISH = getSpacingAndPunctuations(Locale.ENGLISH); diff --git a/tests/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerServiceTest.java b/tests/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerServiceTest.java index 2272d6ba0..e7c48da19 100644 --- a/tests/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerServiceTest.java +++ b/tests/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerServiceTest.java @@ -53,14 +53,8 @@ public class AndroidSpellCheckerServiceTest extends InputTestsBase { sleep(1000); final SpanGetter span = new SpanGetter(mEditText.getText(), SuggestionSpan.class); - // If no span, the following will crash - final String[] suggestions = span.getSuggestions(); - // For this test we consider "годп" should yield at least 2 suggestions (at this moment - // it yields 5). - assertTrue(suggestions.length >= 2); - // We also assume the top suggestion should be "года", which is the top word in the - // Russian dictionary. - assertEquals("", "года", suggestions[0]); + // We don't ship with Russian LM + assertNull(span.getSpan()); } public void testSpellcheckWithPeriods() { diff --git a/tests/src/com/android/inputmethod/latin/spellcheck/UserDictionaryLookupTest.java b/tests/src/com/android/inputmethod/latin/spellcheck/UserDictionaryLookupTest.java new file mode 100644 index 000000000..e5c813942 --- /dev/null +++ b/tests/src/com/android/inputmethod/latin/spellcheck/UserDictionaryLookupTest.java @@ -0,0 +1,279 @@ +/* + * Copyright (C) 2015 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.spellcheck; + +import android.annotation.SuppressLint; +import android.content.ContentResolver; +import android.database.Cursor; +import android.net.Uri; +import android.provider.UserDictionary; +import android.test.AndroidTestCase; +import android.test.suitebuilder.annotation.SmallTest; +import android.util.Log; + +import java.util.HashSet; +import java.util.Locale; + +/** + * Unit tests for {@link UserDictionaryLookup}. + * + * Note, this test doesn't mock out the ContentResolver, in order to make sure UserDictionaryLookup + * works in a real setting. + */ +@SmallTest +public class UserDictionaryLookupTest extends AndroidTestCase { + private static final String TAG = UserDictionaryLookupTest.class.getSimpleName(); + + private ContentResolver mContentResolver; + private HashSet<Uri> mAddedBackup; + + @Override + protected void setUp() throws Exception { + super.setUp(); + mContentResolver = mContext.getContentResolver(); + mAddedBackup = new HashSet<Uri>(); + } + + @Override + protected void tearDown() throws Exception { + // Remove all entries added during this test. + for (Uri row : mAddedBackup) { + mContentResolver.delete(row, null, null); + } + mAddedBackup.clear(); + + super.tearDown(); + } + + /** + * Adds the given word to UserDictionary. + * + * @param word the word to add + * @param locale the locale of the word to add + * @param frequency the frequency of the word to add + * @return the Uri for the given word + */ + @SuppressLint("NewApi") + private Uri addWord(final String word, final Locale locale, int frequency) { + // Add the given word for the given locale. + UserDictionary.Words.addWord(mContext, word, frequency, null, locale); + // Obtain an Uri for the given word. + Cursor cursor = mContentResolver.query(UserDictionary.Words.CONTENT_URI, null, + UserDictionary.Words.WORD + "='" + word + "'", null, null); + assertTrue(cursor.moveToFirst()); + Uri uri = Uri.withAppendedPath(UserDictionary.Words.CONTENT_URI, + cursor.getString(cursor.getColumnIndex(UserDictionary.Words._ID))); + // Add the row to the backup for later clearing. + mAddedBackup.add(uri); + return uri; + } + + /** + * Deletes the entry for the given word from UserDictionary. + * + * @param uri the Uri for the word as returned by addWord + */ + private void deleteWord(Uri uri) { + // Remove the word from the backup so that it's not cleared again later. + mAddedBackup.remove(uri); + // Remove the word from UserDictionary. + mContentResolver.delete(uri, null, null); + } + + public void testExactLocaleMatch() { + Log.d(TAG, "testExactLocaleMatch"); + + // Insert "Foo" as capitalized in the UserDictionary under en_US locale. + addWord("Foo", Locale.US, 17); + + // Create the UserDictionaryLookup and wait until it's loaded. + UserDictionaryLookup lookup = new UserDictionaryLookup(mContext); + while (!lookup.isLoaded()) { + } + + // Any capitalization variation should match. + assertTrue(lookup.isValidWord("foo", Locale.US)); + assertTrue(lookup.isValidWord("Foo", Locale.US)); + assertTrue(lookup.isValidWord("FOO", Locale.US)); + // But similar looking words don't match. + assertFalse(lookup.isValidWord("fo", Locale.US)); + assertFalse(lookup.isValidWord("fop", Locale.US)); + assertFalse(lookup.isValidWord("fooo", Locale.US)); + // Other locales, including more general locales won't match. + assertFalse(lookup.isValidWord("foo", Locale.ENGLISH)); + assertFalse(lookup.isValidWord("foo", Locale.UK)); + assertFalse(lookup.isValidWord("foo", Locale.FRENCH)); + assertFalse(lookup.isValidWord("foo", new Locale(""))); + + lookup.close(); + } + + public void testSubLocaleMatch() { + Log.d(TAG, "testSubLocaleMatch"); + + // Insert "Foo" as capitalized in the UserDictionary under the en locale. + addWord("Foo", Locale.ENGLISH, 17); + + // Create the UserDictionaryLookup and wait until it's loaded. + UserDictionaryLookup lookup = new UserDictionaryLookup(mContext); + while (!lookup.isLoaded()) { + } + + // Any capitalization variation should match for both en and en_US. + assertTrue(lookup.isValidWord("foo", Locale.ENGLISH)); + assertTrue(lookup.isValidWord("foo", Locale.US)); + assertTrue(lookup.isValidWord("Foo", Locale.US)); + assertTrue(lookup.isValidWord("FOO", Locale.US)); + // But similar looking words don't match. + assertFalse(lookup.isValidWord("fo", Locale.US)); + assertFalse(lookup.isValidWord("fop", Locale.US)); + assertFalse(lookup.isValidWord("fooo", Locale.US)); + + lookup.close(); + } + + public void testAllLocalesMatch() { + Log.d(TAG, "testAllLocalesMatch"); + + // Insert "Foo" as capitalized in the UserDictionary under the all locales. + addWord("Foo", null, 17); + + // Create the UserDictionaryLookup and wait until it's loaded. + UserDictionaryLookup lookup = new UserDictionaryLookup(mContext); + while (!lookup.isLoaded()) { + } + + // Any capitalization variation should match for fr, en and en_US. + assertTrue(lookup.isValidWord("foo", new Locale(""))); + assertTrue(lookup.isValidWord("foo", Locale.FRENCH)); + assertTrue(lookup.isValidWord("foo", Locale.ENGLISH)); + assertTrue(lookup.isValidWord("foo", Locale.US)); + assertTrue(lookup.isValidWord("Foo", Locale.US)); + assertTrue(lookup.isValidWord("FOO", Locale.US)); + // But similar looking words don't match. + assertFalse(lookup.isValidWord("fo", Locale.US)); + assertFalse(lookup.isValidWord("fop", Locale.US)); + assertFalse(lookup.isValidWord("fooo", Locale.US)); + + lookup.close(); + } + + public void testMultipleLocalesMatch() { + Log.d(TAG, "testMultipleLocalesMatch"); + + // Insert "Foo" as capitalized in the UserDictionary under the en_US and en_CA and fr + // locales. + addWord("Foo", Locale.US, 17); + addWord("foO", Locale.CANADA, 17); + addWord("fOo", Locale.FRENCH, 17); + + // Create the UserDictionaryLookup and wait until it's loaded. + UserDictionaryLookup lookup = new UserDictionaryLookup(mContext); + while (!lookup.isLoaded()) { + } + + // Both en_CA and en_US match. + assertTrue(lookup.isValidWord("foo", Locale.CANADA)); + assertTrue(lookup.isValidWord("foo", Locale.US)); + assertTrue(lookup.isValidWord("foo", Locale.FRENCH)); + // Other locales, including more general locales won't match. + assertFalse(lookup.isValidWord("foo", Locale.ENGLISH)); + assertFalse(lookup.isValidWord("foo", Locale.UK)); + assertFalse(lookup.isValidWord("foo", new Locale(""))); + + lookup.close(); + } + + public void testReload() { + Log.d(TAG, "testReload"); + + // Insert "foo". + Uri uri = addWord("foo", Locale.US, 17); + + // Create the UserDictionaryLookup and wait until it's loaded. + UserDictionaryLookup lookup = new UserDictionaryLookup(mContext); + while (!lookup.isLoaded()) { + } + + // "foo" should match. + assertTrue(lookup.isValidWord("foo", Locale.US)); + + // "bar" shouldn't match. + assertFalse(lookup.isValidWord("bar", Locale.US)); + + // Now delete "foo" and add "bar". + deleteWord(uri); + addWord("bar", Locale.US, 18); + + // Wait a little bit before expecting a change. The time we wait should be greater than + // UserDictionaryLookup.RELOAD_DELAY_MS. + try { + Thread.sleep(UserDictionaryLookup.RELOAD_DELAY_MS + 1000); + } catch (InterruptedException e) { + } + + // Perform lookups again. Reload should have occured. + // + // "foo" should not match. + assertFalse(lookup.isValidWord("foo", Locale.US)); + + // "bar" should match. + assertTrue(lookup.isValidWord("bar", Locale.US)); + + lookup.close(); + } + + public void testClose() { + Log.d(TAG, "testClose"); + + // Insert "foo". + Uri uri = addWord("foo", Locale.US, 17); + + // Create the UserDictionaryLookup and wait until it's loaded. + UserDictionaryLookup lookup = new UserDictionaryLookup(mContext); + while (!lookup.isLoaded()) { + } + + // "foo" should match. + assertTrue(lookup.isValidWord("foo", Locale.US)); + + // "bar" shouldn't match. + assertFalse(lookup.isValidWord("bar", Locale.US)); + + // Now close (prevents further reloads). + lookup.close(); + + // Now delete "foo" and add "bar". + deleteWord(uri); + addWord("bar", Locale.US, 18); + + // Wait a little bit before expecting a change. The time we wait should be greater than + // UserDictionaryLookup.RELOAD_DELAY_MS. + try { + Thread.sleep(UserDictionaryLookup.RELOAD_DELAY_MS + 1000); + } catch (InterruptedException e) { + } + + // Perform lookups again. Reload should not have occurred. + // + // "foo" should stil match. + assertTrue(lookup.isValidWord("foo", Locale.US)); + + // "bar" should still not match. + assertFalse(lookup.isValidWord("bar", Locale.US)); + } +} diff --git a/tests/src/com/android/inputmethod/latin/touchinputconsumer/NullGestureConsumerTests.java b/tests/src/com/android/inputmethod/latin/touchinputconsumer/NullGestureConsumerTests.java new file mode 100644 index 000000000..ad6bcc3c4 --- /dev/null +++ b/tests/src/com/android/inputmethod/latin/touchinputconsumer/NullGestureConsumerTests.java @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.inputmethod.latin.touchinputconsumer; + +import android.test.AndroidTestCase; +import android.test.suitebuilder.annotation.SmallTest; + +/** + * Tests for GestureConsumer.NULL_GESTURE_CONSUMER. + */ +@SmallTest +public class NullGestureConsumerTests extends AndroidTestCase { + /** + * Tests that GestureConsumer.NULL_GESTURE_CONSUMER indicates that it won't consume gesture data + * and that its methods don't raise exceptions even for invalid data. + */ + public void testNullGestureConsumer() { + assertFalse(GestureConsumer.NULL_GESTURE_CONSUMER.willConsume()); + GestureConsumer.NULL_GESTURE_CONSUMER.onInit(null, null); + GestureConsumer.NULL_GESTURE_CONSUMER.onGestureStarted(null, null); + GestureConsumer.NULL_GESTURE_CONSUMER.onGestureCanceled(); + GestureConsumer.NULL_GESTURE_CONSUMER.onGestureCompleted(null); + GestureConsumer.NULL_GESTURE_CONSUMER.onImeSuggestionsProcessed(null, -1, -1, null); + } + + /** + * Tests that newInstance returns NULL_GESTURE_CONSUMER for invalid input. + */ + public void testNewInstanceGivesNullGestureConsumerForInvalidInputs() { + assertSame(GestureConsumer.NULL_GESTURE_CONSUMER, + GestureConsumer.newInstance(null, null, null, null)); + } +} diff --git a/tests/src/com/android/inputmethod/latin/utils/AdditionalSubtypeUtilsTests.java b/tests/src/com/android/inputmethod/latin/utils/AdditionalSubtypeUtilsTests.java index 91c9c3775..1db839506 100644 --- a/tests/src/com/android/inputmethod/latin/utils/AdditionalSubtypeUtilsTests.java +++ b/tests/src/com/android/inputmethod/latin/utils/AdditionalSubtypeUtilsTests.java @@ -16,6 +16,13 @@ package com.android.inputmethod.latin.utils; +import static com.android.inputmethod.latin.common.Constants.Subtype.KEYBOARD_MODE; +import static com.android.inputmethod.latin.common.Constants.Subtype.ExtraValue.ASCII_CAPABLE; +import static com.android.inputmethod.latin.common.Constants.Subtype.ExtraValue.EMOJI_CAPABLE; +import static com.android.inputmethod.latin.common.Constants.Subtype.ExtraValue.IS_ADDITIONAL_SUBTYPE; +import static com.android.inputmethod.latin.common.Constants.Subtype.ExtraValue.KEYBOARD_LAYOUT_SET; +import static com.android.inputmethod.latin.common.Constants.Subtype.ExtraValue.UNTRANSLATABLE_STRING_IN_SUBTYPE_NAME; + import android.content.Context; import android.os.Build; import android.test.AndroidTestCase; @@ -26,14 +33,6 @@ import com.android.inputmethod.compat.InputMethodSubtypeCompatUtils; import java.util.Locale; -import static com.android.inputmethod.latin.Constants.Subtype.KEYBOARD_MODE; -import static com.android.inputmethod.latin.Constants.Subtype.ExtraValue.ASCII_CAPABLE; -import static com.android.inputmethod.latin.Constants.Subtype.ExtraValue.EMOJI_CAPABLE; -import static com.android.inputmethod.latin.Constants.Subtype.ExtraValue.IS_ADDITIONAL_SUBTYPE; -import static com.android.inputmethod.latin.Constants.Subtype.ExtraValue.KEYBOARD_LAYOUT_SET; -import static com.android.inputmethod.latin.Constants.Subtype.ExtraValue - .UNTRANSLATABLE_STRING_IN_SUBTYPE_NAME; - @SmallTest public class AdditionalSubtypeUtilsTests extends AndroidTestCase { @@ -151,25 +150,25 @@ public class AdditionalSubtypeUtilsTests extends AndroidTestCase { } public void testRestorable() { - final InputMethodSubtype EN_UK_DVORAK = + final InputMethodSubtype EN_US_DVORAK = AdditionalSubtypeUtils.createAsciiEmojiCapableAdditionalSubtype( Locale.US.toString(), "dvorak"); final InputMethodSubtype ZZ_AZERTY = AdditionalSubtypeUtils.createAsciiEmojiCapableAdditionalSubtype( SubtypeLocaleUtils.NO_LANGUAGE, "azerty"); - assertEnUsDvorak(EN_UK_DVORAK); + assertEnUsDvorak(EN_US_DVORAK); assertAzerty(ZZ_AZERTY); // Make sure the subtype can be stored and restored in a deterministic manner. - final InputMethodSubtype[] subtypes = { EN_UK_DVORAK, ZZ_AZERTY }; + final InputMethodSubtype[] subtypes = { EN_US_DVORAK, ZZ_AZERTY }; final String prefSubtype = AdditionalSubtypeUtils.createPrefSubtypes(subtypes); final InputMethodSubtype[] restoredSubtypes = AdditionalSubtypeUtils.createAdditionalSubtypesArray(prefSubtype); assertEquals(2, restoredSubtypes.length); - final InputMethodSubtype restored_EN_UK_DVORAK = restoredSubtypes[0]; + final InputMethodSubtype restored_EN_US_DVORAK = restoredSubtypes[0]; final InputMethodSubtype restored_ZZ_AZERTY = restoredSubtypes[1]; - assertEnUsDvorak(restored_EN_UK_DVORAK); + assertEnUsDvorak(restored_EN_US_DVORAK); assertAzerty(restored_ZZ_AZERTY); } } diff --git a/tests/src/com/android/inputmethod/latin/utils/AsyncResultHolderTests.java b/tests/src/com/android/inputmethod/latin/utils/AsyncResultHolderTests.java index 1501e942a..170d64383 100644 --- a/tests/src/com/android/inputmethod/latin/utils/AsyncResultHolderTests.java +++ b/tests/src/com/android/inputmethod/latin/utils/AsyncResultHolderTests.java @@ -22,14 +22,14 @@ import android.util.Log; @MediumTest public class AsyncResultHolderTests extends AndroidTestCase { - private static final String TAG = AsyncResultHolderTests.class.getSimpleName(); + 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, + private static <T> void setAfterGivenTime(final AsyncResultHolder<T> holder, final T value, final long time) { new Thread(new Runnable() { @Override diff --git a/tests/src/com/android/inputmethod/latin/utils/BinaryDictionaryUtilsTests.java b/tests/src/com/android/inputmethod/latin/utils/BinaryDictionaryUtilsTests.java deleted file mode 100644 index a333ee9bc..000000000 --- a/tests/src/com/android/inputmethod/latin/utils/BinaryDictionaryUtilsTests.java +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.inputmethod.latin.utils; - -import android.test.AndroidTestCase; -import android.test.suitebuilder.annotation.LargeTest; - -import com.android.inputmethod.latin.BinaryDictionary; -import com.android.inputmethod.latin.makedict.DictionaryHeader; -import com.android.inputmethod.latin.makedict.FormatSpec; - -import java.io.File; -import java.io.IOException; -import java.util.HashMap; -import java.util.Locale; -import java.util.Map; -import java.util.concurrent.TimeUnit; - -@LargeTest -public class BinaryDictionaryUtilsTests extends AndroidTestCase { - private static final String TEST_DICT_FILE_EXTENSION = ".testDict"; - private static final String TEST_LOCALE = "test"; - - private File createEmptyDictionaryAndGetFile(final String dictId, - final int formatVersion) throws IOException { - if (formatVersion == FormatSpec.VERSION4) { - return createEmptyVer4DictionaryAndGetFile(dictId); - } else { - throw new IOException("Dictionary format version " + formatVersion - + " is not supported."); - } - } - - private File createEmptyVer4DictionaryAndGetFile(final String dictId) throws IOException { - final File file = getDictFile(dictId); - FileUtils.deleteRecursively(file); - Map<String, String> attributeMap = new HashMap<>(); - attributeMap.put(DictionaryHeader.DICTIONARY_ID_KEY, dictId); - attributeMap.put(DictionaryHeader.DICTIONARY_VERSION_KEY, - String.valueOf(TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis()))); - attributeMap.put(DictionaryHeader.USES_FORGETTING_CURVE_KEY, - DictionaryHeader.ATTRIBUTE_VALUE_TRUE); - attributeMap.put(DictionaryHeader.HAS_HISTORICAL_INFO_KEY, - DictionaryHeader.ATTRIBUTE_VALUE_TRUE); - if (BinaryDictionaryUtils.createEmptyDictFile(file.getAbsolutePath(), FormatSpec.VERSION4, - LocaleUtils.constructLocaleFromString(TEST_LOCALE), attributeMap)) { - return file; - } else { - throw new IOException("Empty dictionary " + file.getAbsolutePath() - + " cannot be created."); - } - } - - private File getDictFile(final String dictId) { - return new File(getContext().getCacheDir(), dictId + TEST_DICT_FILE_EXTENSION); - } - - public void testRenameDictionary() { - final int formatVersion = FormatSpec.VERSION4; - File dictFile0 = null; - try { - dictFile0 = createEmptyDictionaryAndGetFile("MoveFromDictionary", formatVersion); - } catch (IOException e) { - fail("IOException while writing an initial dictionary : " + e); - } - final File dictFile1 = getDictFile("MoveToDictionary"); - FileUtils.deleteRecursively(dictFile1); - assertTrue(BinaryDictionaryUtils.renameDict(dictFile0, dictFile1)); - assertFalse(dictFile0.exists()); - assertTrue(dictFile1.exists()); - BinaryDictionary binaryDictionary = new BinaryDictionary(dictFile1.getAbsolutePath(), - 0 /* offset */, dictFile1.length(), true /* useFullEditDistance */, - Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */); - assertTrue(binaryDictionary.isValidDictionary()); - assertTrue(binaryDictionary.getFormatVersion() == formatVersion); - binaryDictionary.close(); - } -} diff --git a/tests/src/com/android/inputmethod/latin/utils/CapsModeUtilsTests.java b/tests/src/com/android/inputmethod/latin/utils/CapsModeUtilsTests.java index c746c8345..9680d85b3 100644 --- a/tests/src/com/android/inputmethod/latin/utils/CapsModeUtilsTests.java +++ b/tests/src/com/android/inputmethod/latin/utils/CapsModeUtilsTests.java @@ -21,8 +21,8 @@ import android.test.AndroidTestCase; import android.test.suitebuilder.annotation.SmallTest; import android.text.TextUtils; +import com.android.inputmethod.latin.common.LocaleUtils; import com.android.inputmethod.latin.settings.SpacingAndPunctuations; -import com.android.inputmethod.latin.utils.LocaleUtils; import java.util.Locale; @@ -124,5 +124,29 @@ public class CapsModeUtilsTests extends AndroidTestCase { allPathsForCaps("Word. ", c | w, sp, false); // Armenian period : capitalize if MODE_SENTENCES allPathsForCaps("Word\u0589 ", c | w | s, sp, false); + + // Test for sentence terminators + sp = job.runInLocale(res, Locale.ENGLISH); + allPathsForCaps("Word? ", c | w | s, sp, false); + allPathsForCaps("Word?", c | w | s, sp, true); + allPathsForCaps("Word?", c, sp, false); + allPathsForCaps("Word! ", c | w | s, sp, false); + allPathsForCaps("Word!", c | w | s, sp, true); + allPathsForCaps("Word!", c, sp, false); + allPathsForCaps("Word; ", c | w, sp, false); + allPathsForCaps("Word;", c | w, sp, true); + allPathsForCaps("Word;", c, sp, false); + // Test for sentence terminators in Greek + sp = job.runInLocale(res, LocaleUtils.constructLocaleFromString("el")); + allPathsForCaps("Word? ", c | w | s, sp, false); + allPathsForCaps("Word?", c | w | s, sp, true); + allPathsForCaps("Word?", c, sp, false); + allPathsForCaps("Word! ", c | w | s, sp, false); + allPathsForCaps("Word!", c | w | s, sp, true); + allPathsForCaps("Word!", c, sp, false); + // In Greek ";" is the question mark and it terminates the sentence + allPathsForCaps("Word; ", c | w | s, sp, false); + allPathsForCaps("Word;", c | w | s, sp, true); + allPathsForCaps("Word;", c, sp, false); } } diff --git a/tests/src/com/android/inputmethod/latin/utils/CollectionUtilsTests.java b/tests/src/com/android/inputmethod/latin/utils/CollectionUtilsTests.java new file mode 100644 index 000000000..47fd5feaa --- /dev/null +++ b/tests/src/com/android/inputmethod/latin/utils/CollectionUtilsTests.java @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.inputmethod.latin.utils; + +import android.test.AndroidTestCase; +import android.test.suitebuilder.annotation.SmallTest; + +import com.android.inputmethod.latin.common.CollectionUtils; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; + +/** + * Tests for {@link CollectionUtils}. + */ +@SmallTest +public class CollectionUtilsTests extends AndroidTestCase { + /** + * Tests that {@link CollectionUtils#arrayAsList(Object[],int,int)} fails as expected + * with some invalid inputs. + */ + public void testArrayAsListFailure() { + final String[] array = { "0", "1" }; + // Negative start + try { + CollectionUtils.arrayAsList(array, -1, 1); + fail("Failed to catch start < 0"); + } catch (final IllegalArgumentException e) { + assertEquals("Invalid start: -1 end: 1 with array.length: 2", e.getMessage()); + } + // start > end + try { + CollectionUtils.arrayAsList(array, 1, -1); + fail("Failed to catch start > end"); + } catch (final IllegalArgumentException e) { + assertEquals("Invalid start: 1 end: -1 with array.length: 2", e.getMessage()); + } + // end > array.length + try { + CollectionUtils.arrayAsList(array, 1, 3); + fail("Failed to catch end > array.length"); + } catch (final IllegalArgumentException e) { + assertEquals("Invalid start: 1 end: 3 with array.length: 2", e.getMessage()); + } + } + + /** + * Tests that {@link CollectionUtils#arrayAsList(Object[],int,int)} gives the expected + * results for a few valid inputs. + */ + public void testArrayAsList() { + final ArrayList<String> empty = new ArrayList<>(); + assertEquals(empty, CollectionUtils.arrayAsList(new String[] { }, 0, 0)); + final String[] array = { "0", "1", "2", "3", "4" }; + assertEquals(empty, CollectionUtils.arrayAsList(array, 0, 0)); + assertEquals(empty, CollectionUtils.arrayAsList(array, 1, 1)); + assertEquals(empty, CollectionUtils.arrayAsList(array, array.length, array.length)); + final ArrayList<String> expected123 = new ArrayList<>(Arrays.asList("1", "2", "3")); + assertEquals(expected123, CollectionUtils.arrayAsList(array, 1, 4)); + } + + /** + * Tests that {@link CollectionUtils#isNullOrEmpty(java.util.Collection)} gives the expected + * results for a few cases. + */ + public void testIsNullOrEmpty() { + assertTrue(CollectionUtils.isNullOrEmpty(null)); + assertTrue(CollectionUtils.isNullOrEmpty(new ArrayList<>())); + assertTrue(CollectionUtils.isNullOrEmpty(Collections.EMPTY_SET)); + assertFalse(CollectionUtils.isNullOrEmpty(Collections.singleton("Not empty"))); + } +} diff --git a/tests/src/com/android/inputmethod/latin/utils/DictionaryInfoUtilsTests.java b/tests/src/com/android/inputmethod/latin/utils/DictionaryInfoUtilsTests.java index 6e716074c..812353cc4 100644 --- a/tests/src/com/android/inputmethod/latin/utils/DictionaryInfoUtilsTests.java +++ b/tests/src/com/android/inputmethod/latin/utils/DictionaryInfoUtilsTests.java @@ -20,6 +20,7 @@ import android.content.res.Resources; import android.test.AndroidTestCase; import android.test.suitebuilder.annotation.SmallTest; +import com.android.inputmethod.latin.common.LocaleUtils; import com.android.inputmethod.latin.settings.SpacingAndPunctuations; import java.util.Locale; @@ -44,4 +45,21 @@ public class DictionaryInfoUtilsTests extends AndroidTestCase { sp)); assertFalse(DictionaryInfoUtils.looksValidForDictionaryInsertion("!!!", sp)); } + + public void testGetMainDictId() { + assertEquals("main:en", + DictionaryInfoUtils.getMainDictId(LocaleUtils.constructLocaleFromString("en"))); + assertEquals("main:en_us", + DictionaryInfoUtils.getMainDictId(LocaleUtils.constructLocaleFromString("en_US"))); + assertEquals("main:en_gb", + DictionaryInfoUtils.getMainDictId(LocaleUtils.constructLocaleFromString("en_GB"))); + + assertEquals("main:es", + DictionaryInfoUtils.getMainDictId(LocaleUtils.constructLocaleFromString("es"))); + assertEquals("main:es_us", + DictionaryInfoUtils.getMainDictId(LocaleUtils.constructLocaleFromString("es_US"))); + + assertEquals("main:en_us_posix", DictionaryInfoUtils.getMainDictId( + LocaleUtils.constructLocaleFromString("en_US_POSIX"))); + } } diff --git a/tests/src/com/android/inputmethod/latin/utils/EditDistanceTests.java b/tests/src/com/android/inputmethod/latin/utils/EditDistanceTests.java deleted file mode 100644 index 58312264b..000000000 --- a/tests/src/com/android/inputmethod/latin/utils/EditDistanceTests.java +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright (C) 2010 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; - -@SmallTest -public class EditDistanceTests extends AndroidTestCase { - /* - * dist(kitten, sitting) == 3 - * - * kitten- - * .|||.| - * sitting - */ - public void testExample1() { - final int dist = BinaryDictionaryUtils.editDistance("kitten", "sitting"); - assertEquals("edit distance between 'kitten' and 'sitting' is 3", - 3, dist); - } - - /* - * dist(Sunday, Saturday) == 3 - * - * Saturday - * | |.||| - * S--unday - */ - public void testExample2() { - final int dist = BinaryDictionaryUtils.editDistance("Saturday", "Sunday"); - assertEquals("edit distance between 'Saturday' and 'Sunday' is 3", - 3, dist); - } - - public void testBothEmpty() { - final int dist = BinaryDictionaryUtils.editDistance("", ""); - assertEquals("when both string are empty, no edits are needed", - 0, dist); - } - - public void testFirstArgIsEmpty() { - final int dist = BinaryDictionaryUtils.editDistance("", "aaaa"); - assertEquals("when only one string of the arguments is empty," - + " the edit distance is the length of the other.", - 4, dist); - } - - public void testSecoondArgIsEmpty() { - final int dist = BinaryDictionaryUtils.editDistance("aaaa", ""); - assertEquals("when only one string of the arguments is empty," - + " the edit distance is the length of the other.", - 4, dist); - } - - public void testSameStrings() { - final String arg1 = "The quick brown fox jumps over the lazy dog."; - final String arg2 = "The quick brown fox jumps over the lazy dog."; - final int dist = BinaryDictionaryUtils.editDistance(arg1, arg2); - assertEquals("when same strings are passed, distance equals 0.", - 0, dist); - } - - public void testSameReference() { - final String arg = "The quick brown fox jumps over the lazy dog."; - final int dist = BinaryDictionaryUtils.editDistance(arg, arg); - assertEquals("when same string references are passed, the distance equals 0.", - 0, dist); - } - - public void testNullArg() { - try { - BinaryDictionaryUtils.editDistance(null, "aaa"); - fail("IllegalArgumentException should be thrown."); - } catch (Exception e) { - assertTrue(e instanceof IllegalArgumentException); - } - try { - BinaryDictionaryUtils.editDistance("aaa", null); - fail("IllegalArgumentException should be thrown."); - } catch (Exception e) { - assertTrue(e instanceof IllegalArgumentException); - } - } -} diff --git a/tests/src/com/android/inputmethod/latin/utils/ExecutorUtilsTests.java b/tests/src/com/android/inputmethod/latin/utils/ExecutorUtilsTests.java index ae2623d12..86923059b 100644 --- a/tests/src/com/android/inputmethod/latin/utils/ExecutorUtilsTests.java +++ b/tests/src/com/android/inputmethod/latin/utils/ExecutorUtilsTests.java @@ -25,18 +25,18 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; /** - * Unit tests for ExecutorUtils. + * Unit tests for {@link ExecutorUtils}. */ @MediumTest public class ExecutorUtilsTests extends AndroidTestCase { private static final String TAG = ExecutorUtilsTests.class.getSimpleName(); - private static final String TEST_EXECUTOR_ID = "test"; private static final int NUM_OF_TASKS = 10; private static final int DELAY_FOR_WAITING_TASKS_MILLISECONDS = 500; public void testExecute() { - final ExecutorService executor = ExecutorUtils.getExecutor(TEST_EXECUTOR_ID); + final ExecutorService executor = + ExecutorUtils.getBackgroundExecutor(ExecutorUtils.KEYBOARD); final AtomicInteger v = new AtomicInteger(0); for (int i = 0; i < NUM_OF_TASKS; ++i) { executor.execute(new Runnable() { diff --git a/tests/src/com/android/inputmethod/latin/utils/ImportantNoticeUtilsTests.java b/tests/src/com/android/inputmethod/latin/utils/ImportantNoticeUtilsTests.java index 819d76328..e361c7704 100644 --- a/tests/src/com/android/inputmethod/latin/utils/ImportantNoticeUtilsTests.java +++ b/tests/src/com/android/inputmethod/latin/utils/ImportantNoticeUtilsTests.java @@ -16,24 +16,32 @@ package com.android.inputmethod.latin.utils; -import static com.android.inputmethod.latin.utils.ImportantNoticeUtils.KEY_TIMESTAMP_OF_FIRST_IMPORTANT_NOTICE; import static com.android.inputmethod.latin.utils.ImportantNoticeUtils.KEY_IMPORTANT_NOTICE_VERSION; +import static com.android.inputmethod.latin.utils.ImportantNoticeUtils.KEY_TIMESTAMP_OF_FIRST_IMPORTANT_NOTICE; +import static org.mockito.Mockito.when; import android.content.Context; import android.content.SharedPreferences; import android.test.AndroidTestCase; -import android.test.suitebuilder.annotation.SmallTest; +import android.test.suitebuilder.annotation.MediumTest; import android.text.TextUtils; +import com.android.inputmethod.latin.settings.SettingsValues; + +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + import java.util.concurrent.TimeUnit; -@SmallTest +@MediumTest public class ImportantNoticeUtilsTests extends AndroidTestCase { // This should be aligned with R.integer.config_important_notice_version. private static final int CURRENT_IMPORTANT_NOTICE_VERSION = 1; private ImportantNoticePreferences mImportantNoticePreferences; + @Mock private SettingsValues mMockSettingsValues; + private static class ImportantNoticePreferences { private final SharedPreferences mPref; @@ -97,8 +105,10 @@ public class ImportantNoticeUtilsTests extends AndroidTestCase { @Override protected void setUp() throws Exception { super.setUp(); + MockitoAnnotations.initMocks(this); mImportantNoticePreferences = new ImportantNoticePreferences(getContext()); mImportantNoticePreferences.save(); + when(mMockSettingsValues.isPersonalizationEnabled()).thenReturn(true); } @Override @@ -112,11 +122,33 @@ public class ImportantNoticeUtilsTests extends AndroidTestCase { ImportantNoticeUtils.getCurrentImportantNoticeVersion(getContext())); } + public void testStateAfterFreshInstall() { + mImportantNoticePreferences.clear(); + + // Check internal state of {@link ImportantNoticeUtils.shouldShowImportantNotice(Context)} + // after fresh install. + assertEquals("Has new important notice after fresh install", true, + ImportantNoticeUtils.hasNewImportantNotice(getContext())); + assertEquals("Next important notice title after fresh install", false, TextUtils.isEmpty( + ImportantNoticeUtils.getNextImportantNoticeTitle(getContext()))); + assertEquals("Is in system setup wizard after fresh install", false, + ImportantNoticeUtils.isInSystemSetupWizard(getContext())); + final long currentTimeMillis = System.currentTimeMillis(); + assertEquals("Has timeout passed after fresh install", false, + ImportantNoticeUtils.hasTimeoutPassed(getContext(), currentTimeMillis)); + assertEquals("Timestamp of first important notice after fresh install", + (Long)currentTimeMillis, + mImportantNoticePreferences.getLong(KEY_TIMESTAMP_OF_FIRST_IMPORTANT_NOTICE)); + + assertEquals("Current boolean before update", true, + ImportantNoticeUtils.shouldShowImportantNotice(getContext(), mMockSettingsValues)); + } + public void testUpdateVersion() { mImportantNoticePreferences.clear(); assertEquals("Current boolean before update", true, - ImportantNoticeUtils.shouldShowImportantNotice(getContext())); + ImportantNoticeUtils.shouldShowImportantNotice(getContext(), mMockSettingsValues)); assertEquals("Last version before update", 0, ImportantNoticeUtils.getLastImportantNoticeVersion(getContext())); assertEquals("Next version before update ", 1, @@ -129,7 +161,7 @@ public class ImportantNoticeUtilsTests extends AndroidTestCase { ImportantNoticeUtils.updateLastImportantNoticeVersion(getContext()); assertEquals("Current boolean after update", false, - ImportantNoticeUtils.shouldShowImportantNotice(getContext())); + ImportantNoticeUtils.shouldShowImportantNotice(getContext(), mMockSettingsValues)); assertEquals("Last version after update", 1, ImportantNoticeUtils.getLastImportantNoticeVersion(getContext())); assertEquals("Next version after update", 2, @@ -158,12 +190,12 @@ public class ImportantNoticeUtilsTests extends AndroidTestCase { // Call {@link ImportantNoticeUtils#shouldShowImportantNotice(Context)} before timeout. assertEquals("Current boolean before timeout 1", true, - ImportantNoticeUtils.shouldShowImportantNotice(getContext())); + ImportantNoticeUtils.shouldShowImportantNotice(getContext(), mMockSettingsValues)); assertEquals("Last version before timeout 1", 0, ImportantNoticeUtils.getLastImportantNoticeVersion(getContext())); assertEquals("Next version before timeout 1", 1, ImportantNoticeUtils.getNextImportantNoticeVersion(getContext())); - assertEquals("Last time before timeout 1", (Long)lastTime, + assertEquals("Timestamp of first important notice before timeout 1", (Long)lastTime, mImportantNoticePreferences.getLong(KEY_TIMESTAMP_OF_FIRST_IMPORTANT_NOTICE)); assertEquals("Current title before timeout 1", false, TextUtils.isEmpty( ImportantNoticeUtils.getNextImportantNoticeTitle(getContext()))); @@ -175,12 +207,12 @@ public class ImportantNoticeUtilsTests extends AndroidTestCase { // Call {@link ImportantNoticeUtils#shouldShowImportantNotice(Context)} before timeout // again. assertEquals("Current boolean before timeout 2", true, - ImportantNoticeUtils.shouldShowImportantNotice(getContext())); + ImportantNoticeUtils.shouldShowImportantNotice(getContext(), mMockSettingsValues)); assertEquals("Last version before timeout 2", 0, ImportantNoticeUtils.getLastImportantNoticeVersion(getContext())); assertEquals("Next version before timeout 2", 1, ImportantNoticeUtils.getNextImportantNoticeVersion(getContext())); - assertEquals("Last time before timeout 2", (Long)lastTime, + assertEquals("Timestamp of first important notice before timeout 2", (Long)lastTime, mImportantNoticePreferences.getLong(KEY_TIMESTAMP_OF_FIRST_IMPORTANT_NOTICE)); assertEquals("Current title before timeout 2", false, TextUtils.isEmpty( ImportantNoticeUtils.getNextImportantNoticeTitle(getContext()))); @@ -191,12 +223,12 @@ public class ImportantNoticeUtilsTests extends AndroidTestCase { // Call {@link ImportantNoticeUtils#shouldShowImportantNotice(Context)} after timeout. assertEquals("Current boolean after timeout 1", false, - ImportantNoticeUtils.shouldShowImportantNotice(getContext())); + ImportantNoticeUtils.shouldShowImportantNotice(getContext(), mMockSettingsValues)); assertEquals("Last version after timeout 1", 1, ImportantNoticeUtils.getLastImportantNoticeVersion(getContext())); assertEquals("Next version after timeout 1", 2, ImportantNoticeUtils.getNextImportantNoticeVersion(getContext())); - assertEquals("Last time aflter timeout 1", null, + assertEquals("Timestamp of first important notice after timeout 1", null, mImportantNoticePreferences.getLong(KEY_TIMESTAMP_OF_FIRST_IMPORTANT_NOTICE)); assertEquals("Current title after timeout 1", true, TextUtils.isEmpty( ImportantNoticeUtils.getNextImportantNoticeTitle(getContext()))); @@ -207,16 +239,30 @@ public class ImportantNoticeUtilsTests extends AndroidTestCase { // Call {@link ImportantNoticeUtils#shouldShowImportantNotice(Context)} after timeout again. assertEquals("Current boolean after timeout 2", false, - ImportantNoticeUtils.shouldShowImportantNotice(getContext())); + ImportantNoticeUtils.shouldShowImportantNotice(getContext(), mMockSettingsValues)); assertEquals("Last version after timeout 2", 1, ImportantNoticeUtils.getLastImportantNoticeVersion(getContext())); assertEquals("Next version after timeout 2", 2, ImportantNoticeUtils.getNextImportantNoticeVersion(getContext())); - assertEquals("Last time aflter timeout 2", null, + assertEquals("Timestamp of first important notice after timeout 2", null, mImportantNoticePreferences.getLong(KEY_TIMESTAMP_OF_FIRST_IMPORTANT_NOTICE)); assertEquals("Current title after timeout 2", true, TextUtils.isEmpty( ImportantNoticeUtils.getNextImportantNoticeTitle(getContext()))); assertEquals("Current contents after timeout 2", true, TextUtils.isEmpty( ImportantNoticeUtils.getNextImportantNoticeContents(getContext()))); } + + public void testPersonalizationSetting() { + mImportantNoticePreferences.clear(); + + // Personalization enabled. + when(mMockSettingsValues.isPersonalizationEnabled()).thenReturn(true); + assertEquals("Current boolean with personalization enabled", true, + ImportantNoticeUtils.shouldShowImportantNotice(getContext(), mMockSettingsValues)); + + // Personalization disabled. + when(mMockSettingsValues.isPersonalizationEnabled()).thenReturn(false); + assertEquals("Current boolean with personalization disabled", false, + ImportantNoticeUtils.shouldShowImportantNotice(getContext(), mMockSettingsValues)); + } } diff --git a/tests/src/com/android/inputmethod/latin/utils/JsonUtilsTests.java b/tests/src/com/android/inputmethod/latin/utils/JsonUtilsTests.java new file mode 100644 index 000000000..194112070 --- /dev/null +++ b/tests/src/com/android/inputmethod/latin/utils/JsonUtilsTests.java @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.inputmethod.latin.utils; + +import android.test.AndroidTestCase; +import android.test.suitebuilder.annotation.SmallTest; + +import java.util.Arrays; +import java.util.List; + +@SmallTest +public class JsonUtilsTests extends AndroidTestCase { + public void testJsonUtils() { + final Object[] objs = new Object[] { 1, "aaa", "bbb", 3 }; + final List<Object> objArray = Arrays.asList(objs); + final String str = JsonUtils.listToJsonStr(objArray); + final List<Object> newObjArray = JsonUtils.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/LanguageOnSpacebarUtilsTests.java b/tests/src/com/android/inputmethod/latin/utils/LanguageOnSpacebarUtilsTests.java new file mode 100644 index 000000000..e4b6a668c --- /dev/null +++ b/tests/src/com/android/inputmethod/latin/utils/LanguageOnSpacebarUtilsTests.java @@ -0,0 +1,215 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.inputmethod.latin.utils; + +import static com.android.inputmethod.latin.utils.LanguageOnSpacebarUtils.FORMAT_TYPE_FULL_LOCALE; +import static com.android.inputmethod.latin.utils.LanguageOnSpacebarUtils.FORMAT_TYPE_LANGUAGE_ONLY; +import static com.android.inputmethod.latin.utils.LanguageOnSpacebarUtils.FORMAT_TYPE_NONE; + +import android.content.Context; +import android.test.AndroidTestCase; +import android.test.suitebuilder.annotation.SmallTest; +import android.view.inputmethod.InputMethodSubtype; + +import com.android.inputmethod.latin.RichInputMethodManager; +import com.android.inputmethod.latin.RichInputMethodSubtype; +import com.android.inputmethod.latin.utils.AdditionalSubtypeUtils; +import com.android.inputmethod.latin.utils.LanguageOnSpacebarUtils; +import com.android.inputmethod.latin.utils.SubtypeLocaleUtils; + +import java.util.ArrayList; +import java.util.Locale; + +import javax.annotation.Nonnull; + +@SmallTest +public class LanguageOnSpacebarUtilsTests extends AndroidTestCase { + private RichInputMethodManager mRichImm; + + RichInputMethodSubtype EN_US_QWERTY; + RichInputMethodSubtype EN_GB_QWERTY; + RichInputMethodSubtype FR_AZERTY; + RichInputMethodSubtype FR_CA_QWERTY; + RichInputMethodSubtype FR_CH_SWISS; + RichInputMethodSubtype FR_CH_QWERTY; + RichInputMethodSubtype FR_CH_QWERTZ; + RichInputMethodSubtype IW_HEBREW; + RichInputMethodSubtype ZZ_QWERTY; + + @Override + protected void setUp() throws Exception { + super.setUp(); + final Context context = getContext(); + RichInputMethodManager.init(context); + mRichImm = RichInputMethodManager.getInstance(); + + EN_US_QWERTY = findSubtypeOf(Locale.US.toString(), "qwerty"); + EN_GB_QWERTY = findSubtypeOf(Locale.UK.toString(), "qwerty"); + FR_AZERTY = findSubtypeOf(Locale.FRENCH.toString(), "azerty"); + FR_CA_QWERTY = findSubtypeOf(Locale.CANADA_FRENCH.toString(), "qwerty"); + FR_CH_SWISS = findSubtypeOf("fr_CH", "swiss"); + FR_CH_QWERTZ = new RichInputMethodSubtype( + AdditionalSubtypeUtils.createAsciiEmojiCapableAdditionalSubtype("fr_CH", "qwertz")); + FR_CH_QWERTY = new RichInputMethodSubtype( + AdditionalSubtypeUtils.createAsciiEmojiCapableAdditionalSubtype("fr_CH", "qwerty")); + IW_HEBREW = findSubtypeOf("iw", "hebrew"); + ZZ_QWERTY = findSubtypeOf(SubtypeLocaleUtils.NO_LANGUAGE, "qwerty"); + } + + @Nonnull + private RichInputMethodSubtype findSubtypeOf(final String localeString, + final String keyboardLayoutSetName) { + final InputMethodSubtype subtype = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet( + localeString, keyboardLayoutSetName); + if (subtype == null) { + throw new RuntimeException("Can't find subtype of " + localeString + " with " + + keyboardLayoutSetName); + } + return new RichInputMethodSubtype(subtype); + } + + private static void enableSubtypes(final RichInputMethodSubtype ... subtypes) { + final ArrayList<InputMethodSubtype> enabledSubtypes = new ArrayList<>(); + for (final RichInputMethodSubtype subtype : subtypes) { + enabledSubtypes.add(subtype.getRawSubtype()); + } + LanguageOnSpacebarUtils.setEnabledSubtypes(enabledSubtypes); + } + + private static void assertFormatType(final RichInputMethodSubtype subtype, + final boolean implicitlyEnabledSubtype, final Locale systemLocale, + final int expectedFormat) { + LanguageOnSpacebarUtils.onSubtypeChanged(subtype, implicitlyEnabledSubtype, systemLocale); + assertEquals(subtype.getLocale() + " implicitly=" + implicitlyEnabledSubtype + + " in " + systemLocale, expectedFormat, + LanguageOnSpacebarUtils.getLanguageOnSpacebarFormatType(subtype)); + } + + public void testOneSubtypeImplicitlyEnabled() { + enableSubtypes(EN_US_QWERTY); + assertFormatType(EN_US_QWERTY, true, Locale.US, FORMAT_TYPE_NONE); + + enableSubtypes(EN_GB_QWERTY); + assertFormatType(EN_GB_QWERTY, true, Locale.UK, FORMAT_TYPE_NONE); + + enableSubtypes(FR_AZERTY); + assertFormatType(FR_AZERTY, true, Locale.FRANCE, FORMAT_TYPE_NONE); + + enableSubtypes(FR_CA_QWERTY); + assertFormatType(FR_CA_QWERTY, true, Locale.CANADA_FRENCH, FORMAT_TYPE_NONE); + } + + public void testOneSubtypeExplicitlyEnabled() { + enableSubtypes(EN_US_QWERTY); + assertFormatType(EN_US_QWERTY, false, Locale.UK, FORMAT_TYPE_LANGUAGE_ONLY); + assertFormatType(EN_US_QWERTY, false, Locale.FRANCE, FORMAT_TYPE_LANGUAGE_ONLY); + + enableSubtypes(EN_GB_QWERTY); + assertFormatType(EN_GB_QWERTY, false, Locale.US, FORMAT_TYPE_LANGUAGE_ONLY); + assertFormatType(EN_GB_QWERTY, false, Locale.FRANCE, FORMAT_TYPE_LANGUAGE_ONLY); + + enableSubtypes(FR_AZERTY); + assertFormatType(FR_AZERTY, false, Locale.US, FORMAT_TYPE_LANGUAGE_ONLY); + assertFormatType(FR_AZERTY, false, Locale.CANADA_FRENCH, FORMAT_TYPE_LANGUAGE_ONLY); + + enableSubtypes(FR_CA_QWERTY); + assertFormatType(FR_CA_QWERTY, false, Locale.US, FORMAT_TYPE_LANGUAGE_ONLY); + assertFormatType(FR_CA_QWERTY, false, Locale.FRANCE, FORMAT_TYPE_LANGUAGE_ONLY); + } + + public void testOneSubtypeImplicitlyEnabledWithNoLanguageSubtype() { + final Locale Locale_IW = new Locale("iw"); + enableSubtypes(IW_HEBREW, ZZ_QWERTY); + // TODO: Should this be FORMAT_TYPE_NONE? + assertFormatType(IW_HEBREW, true, Locale_IW, FORMAT_TYPE_LANGUAGE_ONLY); + // TODO: Should this be FORMAT_TYPE_NONE? + assertFormatType(ZZ_QWERTY, true, Locale_IW, FORMAT_TYPE_FULL_LOCALE); + } + + public void testTwoSubtypesExplicitlyEnabled() { + enableSubtypes(EN_US_QWERTY, FR_AZERTY); + assertFormatType(EN_US_QWERTY, false, Locale.US, FORMAT_TYPE_LANGUAGE_ONLY); + assertFormatType(FR_AZERTY, false, Locale.US, FORMAT_TYPE_LANGUAGE_ONLY); + assertFormatType(EN_US_QWERTY, false, Locale.FRANCE, FORMAT_TYPE_LANGUAGE_ONLY); + assertFormatType(FR_AZERTY, false, Locale.FRANCE, FORMAT_TYPE_LANGUAGE_ONLY); + assertFormatType(EN_US_QWERTY, false, Locale.JAPAN, FORMAT_TYPE_LANGUAGE_ONLY); + assertFormatType(FR_AZERTY, false, Locale.JAPAN, FORMAT_TYPE_LANGUAGE_ONLY); + + enableSubtypes(EN_US_QWERTY, ZZ_QWERTY); + assertFormatType(EN_US_QWERTY, false, Locale.US, FORMAT_TYPE_LANGUAGE_ONLY); + assertFormatType(ZZ_QWERTY, false, Locale.US, FORMAT_TYPE_FULL_LOCALE); + assertFormatType(EN_US_QWERTY, false, Locale.FRANCE, FORMAT_TYPE_LANGUAGE_ONLY); + assertFormatType(ZZ_QWERTY, false, Locale.FRANCE, FORMAT_TYPE_FULL_LOCALE); + + } + + public void testMultiSubtypeWithSameLanuageAndSameLayout() { + // Explicitly enable en_US, en_GB, fr_FR, and no language keyboards. + enableSubtypes(EN_US_QWERTY, EN_GB_QWERTY, FR_CA_QWERTY, ZZ_QWERTY); + + assertFormatType(EN_US_QWERTY, false, Locale.US, FORMAT_TYPE_FULL_LOCALE); + assertFormatType(EN_GB_QWERTY, false, Locale.US, FORMAT_TYPE_FULL_LOCALE); + assertFormatType(FR_CA_QWERTY, false, Locale.US, FORMAT_TYPE_LANGUAGE_ONLY); + assertFormatType(ZZ_QWERTY, false, Locale.US, FORMAT_TYPE_FULL_LOCALE); + + assertFormatType(EN_US_QWERTY, false, Locale.JAPAN, FORMAT_TYPE_FULL_LOCALE); + assertFormatType(EN_GB_QWERTY, false, Locale.JAPAN, FORMAT_TYPE_FULL_LOCALE); + assertFormatType(FR_CA_QWERTY, false, Locale.JAPAN, FORMAT_TYPE_LANGUAGE_ONLY); + assertFormatType(ZZ_QWERTY, false, Locale.JAPAN, FORMAT_TYPE_FULL_LOCALE); + } + + public void testMultiSubtypesWithSameLanguageButHaveDifferentLayout() { + enableSubtypes(FR_AZERTY, FR_CA_QWERTY, FR_CH_SWISS, FR_CH_QWERTZ); + + assertFormatType(FR_AZERTY, false, Locale.FRANCE, FORMAT_TYPE_LANGUAGE_ONLY); + assertFormatType(FR_CA_QWERTY, false, Locale.FRANCE, FORMAT_TYPE_LANGUAGE_ONLY); + assertFormatType(FR_CH_SWISS, false, Locale.FRANCE, FORMAT_TYPE_LANGUAGE_ONLY); + assertFormatType(FR_CH_QWERTZ, false, Locale.FRANCE, FORMAT_TYPE_LANGUAGE_ONLY); + + assertFormatType(FR_AZERTY, false, Locale.CANADA_FRENCH, FORMAT_TYPE_LANGUAGE_ONLY); + assertFormatType(FR_CA_QWERTY, false, Locale.CANADA_FRENCH, FORMAT_TYPE_LANGUAGE_ONLY); + assertFormatType(FR_CH_SWISS, false, Locale.CANADA_FRENCH, FORMAT_TYPE_LANGUAGE_ONLY); + assertFormatType(FR_CH_QWERTZ, false, Locale.CANADA_FRENCH, FORMAT_TYPE_LANGUAGE_ONLY); + + assertFormatType(FR_AZERTY, false, Locale.JAPAN, FORMAT_TYPE_LANGUAGE_ONLY); + assertFormatType(FR_CA_QWERTY, false, Locale.JAPAN, FORMAT_TYPE_LANGUAGE_ONLY); + assertFormatType(FR_CH_SWISS, false, Locale.JAPAN, FORMAT_TYPE_LANGUAGE_ONLY); + assertFormatType(FR_CH_QWERTZ, false, Locale.JAPAN, FORMAT_TYPE_LANGUAGE_ONLY); + } + + public void testMultiSubtypesWithSameLanguageAndMayHaveSameLayout() { + enableSubtypes(FR_AZERTY, FR_CA_QWERTY, FR_CH_SWISS, FR_CH_QWERTY, FR_CH_QWERTZ); + + assertFormatType(FR_AZERTY, false, Locale.FRANCE, FORMAT_TYPE_LANGUAGE_ONLY); + assertFormatType(FR_CA_QWERTY, false, Locale.FRANCE, FORMAT_TYPE_FULL_LOCALE); + assertFormatType(FR_CH_SWISS, false, Locale.FRANCE, FORMAT_TYPE_LANGUAGE_ONLY); + assertFormatType(FR_CH_QWERTY, false, Locale.FRANCE, FORMAT_TYPE_FULL_LOCALE); + assertFormatType(FR_CH_QWERTZ, false, Locale.FRANCE, FORMAT_TYPE_LANGUAGE_ONLY); + + assertFormatType(FR_AZERTY, false, Locale.CANADA_FRENCH, FORMAT_TYPE_LANGUAGE_ONLY); + assertFormatType(FR_CA_QWERTY, false, Locale.CANADA_FRENCH, FORMAT_TYPE_FULL_LOCALE); + assertFormatType(FR_CH_SWISS, false, Locale.CANADA_FRENCH, FORMAT_TYPE_LANGUAGE_ONLY); + assertFormatType(FR_CH_QWERTY, false, Locale.CANADA_FRENCH, FORMAT_TYPE_FULL_LOCALE); + assertFormatType(FR_CH_QWERTZ, false, Locale.CANADA_FRENCH, FORMAT_TYPE_LANGUAGE_ONLY); + + assertFormatType(FR_AZERTY, false, Locale.JAPAN, FORMAT_TYPE_LANGUAGE_ONLY); + assertFormatType(FR_CA_QWERTY, false, Locale.JAPAN, FORMAT_TYPE_FULL_LOCALE); + assertFormatType(FR_CH_SWISS, false, Locale.JAPAN, FORMAT_TYPE_LANGUAGE_ONLY); + assertFormatType(FR_CH_QWERTY, false, Locale.JAPAN, FORMAT_TYPE_FULL_LOCALE); + assertFormatType(FR_CH_QWERTZ, false, Locale.JAPAN, FORMAT_TYPE_LANGUAGE_ONLY); + } +} diff --git a/tests/src/com/android/inputmethod/latin/utils/RecapitalizeStatusTests.java b/tests/src/com/android/inputmethod/latin/utils/RecapitalizeStatusTests.java index a3f2ce586..9b826839f 100644 --- a/tests/src/com/android/inputmethod/latin/utils/RecapitalizeStatusTests.java +++ b/tests/src/com/android/inputmethod/latin/utils/RecapitalizeStatusTests.java @@ -19,7 +19,7 @@ package com.android.inputmethod.latin.utils; import android.test.AndroidTestCase; import android.test.suitebuilder.annotation.SmallTest; -import com.android.inputmethod.latin.Constants; +import com.android.inputmethod.latin.common.Constants; import java.util.Locale; diff --git a/tests/src/com/android/inputmethod/latin/utils/SpacebarLanguagetUtilsTests.java b/tests/src/com/android/inputmethod/latin/utils/SpacebarLanguagetUtilsTests.java deleted file mode 100644 index fdde34251..000000000 --- a/tests/src/com/android/inputmethod/latin/utils/SpacebarLanguagetUtilsTests.java +++ /dev/null @@ -1,251 +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.utils; - -import android.content.Context; -import android.content.res.Resources; -import android.test.AndroidTestCase; -import android.test.suitebuilder.annotation.SmallTest; -import android.view.inputmethod.InputMethodInfo; -import android.view.inputmethod.InputMethodSubtype; - -import com.android.inputmethod.latin.RichInputMethodManager; - -import java.util.ArrayList; -import java.util.Locale; - -@SmallTest -public class SpacebarLanguagetUtilsTests extends AndroidTestCase { - // All input method subtypes of LatinIME. - private final ArrayList<InputMethodSubtype> mSubtypesList = new ArrayList<>(); - - private RichInputMethodManager mRichImm; - private Resources mRes; - - InputMethodSubtype EN_US; - InputMethodSubtype EN_GB; - InputMethodSubtype ES_US; - InputMethodSubtype FR; - InputMethodSubtype FR_CA; - InputMethodSubtype FR_CH; - InputMethodSubtype DE; - InputMethodSubtype DE_CH; - 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); - - final InputMethodInfo imi = mRichImm.getInputMethodInfoOfThisIme(); - final int subtypeCount = imi.getSubtypeCount(); - for (int index = 0; index < subtypeCount; index++) { - final InputMethodSubtype subtype = imi.getSubtypeAt(index); - mSubtypesList.add(subtype); - } - - 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"); - FR_CH = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet( - "fr_CH", "swiss"); - DE = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet( - Locale.GERMAN.toString(), "qwertz"); - DE_CH = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet( - "de_CH", "swiss"); - ZZ = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet( - SubtypeLocaleUtils.NO_LANGUAGE, "qwerty"); - DE_QWERTY = AdditionalSubtypeUtils.createAsciiEmojiCapableAdditionalSubtype( - Locale.GERMAN.toString(), "qwerty"); - FR_QWERTZ = AdditionalSubtypeUtils.createAsciiEmojiCapableAdditionalSubtype( - Locale.FRENCH.toString(), "qwertz"); - EN_US_AZERTY = AdditionalSubtypeUtils.createAsciiEmojiCapableAdditionalSubtype( - Locale.US.toString(), "azerty"); - EN_UK_DVORAK = AdditionalSubtypeUtils.createAsciiEmojiCapableAdditionalSubtype( - Locale.UK.toString(), "dvorak"); - ES_US_COLEMAK = AdditionalSubtypeUtils.createAsciiEmojiCapableAdditionalSubtype( - "es_US", "colemak"); - ZZ_AZERTY = AdditionalSubtypeUtils.createAsciiEmojiCapableAdditionalSubtype( - SubtypeLocaleUtils.NO_LANGUAGE, "azerty"); - ZZ_PC = AdditionalSubtypeUtils.createAsciiEmojiCapableAdditionalSubtype( - SubtypeLocaleUtils.NO_LANGUAGE, "pcqwerty"); - } - - public void testAllFullDisplayNameForSpacebar() { - for (final InputMethodSubtype subtype : mSubtypesList) { - final String subtypeName = SubtypeLocaleUtils - .getSubtypeDisplayNameInSystemLocale(subtype); - final String spacebarText = SpacebarLanguageUtils.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 = SpacebarLanguageUtils.getMiddleDisplayName(subtype); - if (SubtypeLocaleUtils.isNoLanguage(subtype)) { - assertEquals(subtypeName, - SubtypeLocaleUtils.getKeyboardLayoutSetDisplayName(subtype), spacebarText); - } else { - final Locale locale = SubtypeLocaleUtils.getSubtypeLocale(subtype); - assertEquals(subtypeName, - SubtypeLocaleUtils.getSubtypeLocaleDisplayName(locale.getLanguage()), - spacebarText); - } - } - } - - // InputMethodSubtype's display name for spacebar text in its locale. - // isAdditionalSubtype (T=true, F=false) - // locale layout | Middle Full - // ------ ------- - --------- ---------------------- - // en_US qwerty F English English (US) exception - // en_GB qwerty F English English (UK) exception - // es_US spanish F Español Español (EE.UU.) exception - // fr azerty F Français Français - // fr_CA qwerty F Français Français (Canada) - // fr_CH swiss F Français Français (Suisse) - // de qwertz F Deutsch Deutsch - // de_CH swiss F Deutsch Deutsch (Schweiz) - // zz qwerty F QWERTY QWERTY - // fr qwertz T Français Français - // de qwerty T Deutsch Deutsch - // en_US azerty T 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)", - SpacebarLanguageUtils.getFullDisplayName(EN_US)); - assertEquals("en_GB", "English (UK)", - SpacebarLanguageUtils.getFullDisplayName(EN_GB)); - assertEquals("es_US", "Español (EE.UU.)", - SpacebarLanguageUtils.getFullDisplayName(ES_US)); - assertEquals("fr", "Français", - SpacebarLanguageUtils.getFullDisplayName(FR)); - assertEquals("fr_CA", "Français (Canada)", - SpacebarLanguageUtils.getFullDisplayName(FR_CA)); - assertEquals("fr_CH", "Français (Suisse)", - SpacebarLanguageUtils.getFullDisplayName(FR_CH)); - assertEquals("de", "Deutsch", - SpacebarLanguageUtils.getFullDisplayName(DE)); - assertEquals("de_CH", "Deutsch (Schweiz)", - SpacebarLanguageUtils.getFullDisplayName(DE_CH)); - assertEquals("zz", "QWERTY", - SpacebarLanguageUtils.getFullDisplayName(ZZ)); - - assertEquals("en_US", "English", - SpacebarLanguageUtils.getMiddleDisplayName(EN_US)); - assertEquals("en_GB", "English", - SpacebarLanguageUtils.getMiddleDisplayName(EN_GB)); - assertEquals("es_US", "Español", - SpacebarLanguageUtils.getMiddleDisplayName(ES_US)); - assertEquals("fr", "Français", - SpacebarLanguageUtils.getMiddleDisplayName(FR)); - assertEquals("fr_CA", "Français", - SpacebarLanguageUtils.getMiddleDisplayName(FR_CA)); - assertEquals("fr_CH", "Français", - SpacebarLanguageUtils.getMiddleDisplayName(FR_CH)); - assertEquals("de", "Deutsch", - SpacebarLanguageUtils.getMiddleDisplayName(DE)); - assertEquals("de_CH", "Deutsch", - SpacebarLanguageUtils.getMiddleDisplayName(DE_CH)); - assertEquals("zz", "QWERTY", - SpacebarLanguageUtils.getMiddleDisplayName(ZZ)); - return null; - } - }; - - private final RunInLocale<Void> testsAdditionalSubtypesForSpacebar = new RunInLocale<Void>() { - @Override - protected Void job(final Resources res) { - assertEquals("fr qwertz", "Français", - SpacebarLanguageUtils.getFullDisplayName(FR_QWERTZ)); - assertEquals("de qwerty", "Deutsch", - SpacebarLanguageUtils.getFullDisplayName(DE_QWERTY)); - assertEquals("en_US azerty", "English (US)", - SpacebarLanguageUtils.getFullDisplayName(EN_US_AZERTY)); - assertEquals("en_UK dvorak", "English (UK)", - SpacebarLanguageUtils.getFullDisplayName(EN_UK_DVORAK)); - assertEquals("es_US colemak", "Español (EE.UU.)", - SpacebarLanguageUtils.getFullDisplayName(ES_US_COLEMAK)); - assertEquals("zz azerty", "AZERTY", - SpacebarLanguageUtils.getFullDisplayName(ZZ_AZERTY)); - assertEquals("zz pc", "PC", - SpacebarLanguageUtils.getFullDisplayName(ZZ_PC)); - - assertEquals("fr qwertz", "Français", - SpacebarLanguageUtils.getMiddleDisplayName(FR_QWERTZ)); - assertEquals("de qwerty", "Deutsch", - SpacebarLanguageUtils.getMiddleDisplayName(DE_QWERTY)); - assertEquals("en_US azerty", "English", - SpacebarLanguageUtils.getMiddleDisplayName(EN_US_AZERTY)); - assertEquals("en_UK dvorak", "English", - SpacebarLanguageUtils.getMiddleDisplayName(EN_UK_DVORAK)); - assertEquals("es_US colemak", "Español", - SpacebarLanguageUtils.getMiddleDisplayName(ES_US_COLEMAK)); - assertEquals("zz azerty", "AZERTY", - SpacebarLanguageUtils.getMiddleDisplayName(ZZ_AZERTY)); - assertEquals("zz pc", "PC", - SpacebarLanguageUtils.getMiddleDisplayName(ZZ_PC)); - 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/utils/SpannableStringUtilsTests.java b/tests/src/com/android/inputmethod/latin/utils/SpannableStringUtilsTests.java index fa6ad16c1..665d81ccd 100644 --- a/tests/src/com/android/inputmethod/latin/utils/SpannableStringUtilsTests.java +++ b/tests/src/com/android/inputmethod/latin/utils/SpannableStringUtilsTests.java @@ -20,9 +20,10 @@ import android.test.AndroidTestCase; import android.test.suitebuilder.annotation.SmallTest; import android.text.style.SuggestionSpan; import android.text.style.URLSpan; +import android.text.SpannableString; import android.text.SpannableStringBuilder; -import android.text.Spannable; import android.text.Spanned; +import android.text.SpannedString; @SmallTest public class SpannableStringUtilsTests extends AndroidTestCase { @@ -34,8 +35,8 @@ public class SpannableStringUtilsTests extends AndroidTestCase { 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); + new String[] {"" + i}, Spanned.SPAN_PARAGRAPH), + i * 12, i * 12 + 12, Spanned.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. @@ -51,8 +52,175 @@ public class SpannableStringUtilsTests extends AndroidTestCase { 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); + flags & Spanned.SPAN_PARAGRAPH, 0); assertTrue("Should be a SuggestionSpan", spans[i] instanceof SuggestionSpan); } } + + private static void assertSpanCount(final int expectedCount, final CharSequence cs) { + final int actualCount; + if (cs instanceof Spanned) { + final Spanned spanned = (Spanned) cs; + actualCount = spanned.getSpans(0, spanned.length(), Object.class).length; + } else { + actualCount = 0; + } + assertEquals(expectedCount, actualCount); + } + + private static void assertSpan(final CharSequence cs, final Object expectedSpan, + final int expectedStart, final int expectedEnd, final int expectedFlags) { + assertTrue(cs instanceof Spanned); + final Spanned spanned = (Spanned) cs; + final Object[] actualSpans = spanned.getSpans(0, spanned.length(), Object.class); + for (Object actualSpan : actualSpans) { + if (actualSpan == expectedSpan) { + final int actualStart = spanned.getSpanStart(actualSpan); + final int actualEnd = spanned.getSpanEnd(actualSpan); + final int actualFlags = spanned.getSpanFlags(actualSpan); + assertEquals(expectedStart, actualStart); + assertEquals(expectedEnd, actualEnd); + assertEquals(expectedFlags, actualFlags); + return; + } + } + assertTrue(false); + } + + public void testSplitCharSequenceWithSpan() { + // text: " a bcd efg hij " + // span1: ^^^^^^^ + // span2: ^^^^^ + // span3: ^ + final SpannableString spannableString = new SpannableString(" a bcd efg hij "); + final Object span1 = new Object(); + final Object span2 = new Object(); + final Object span3 = new Object(); + final int SPAN1_FLAGS = Spanned.SPAN_EXCLUSIVE_EXCLUSIVE; + final int SPAN2_FLAGS = Spanned.SPAN_EXCLUSIVE_INCLUSIVE; + final int SPAN3_FLAGS = Spanned.SPAN_INCLUSIVE_INCLUSIVE; + spannableString.setSpan(span1, 0, 7, SPAN1_FLAGS); + spannableString.setSpan(span2, 0, 5, SPAN2_FLAGS); + spannableString.setSpan(span3, 12, 13, SPAN3_FLAGS); + final CharSequence[] charSequencesFromSpanned = SpannableStringUtils.split( + spannableString, " ", true /* preserveTrailingEmptySegmengs */); + final CharSequence[] charSequencesFromString = SpannableStringUtils.split( + spannableString.toString(), " ", true /* preserveTrailingEmptySegmengs */); + + + assertEquals(7, charSequencesFromString.length); + assertEquals(7, charSequencesFromSpanned.length); + + // text: "" + // span1: ^ + // span2: ^ + // span3: + assertEquals("", charSequencesFromString[0].toString()); + assertSpanCount(0, charSequencesFromString[0]); + assertEquals("", charSequencesFromSpanned[0].toString()); + assertSpanCount(2, charSequencesFromSpanned[0]); + assertSpan(charSequencesFromSpanned[0], span1, 0, 0, SPAN1_FLAGS); + assertSpan(charSequencesFromSpanned[0], span2, 0, 0, SPAN2_FLAGS); + + // text: "a" + // span1: ^ + // span2: ^ + // span3: + assertEquals("a", charSequencesFromString[1].toString()); + assertSpanCount(0, charSequencesFromString[1]); + assertEquals("a", charSequencesFromSpanned[1].toString()); + assertSpanCount(2, charSequencesFromSpanned[1]); + assertSpan(charSequencesFromSpanned[1], span1, 0, 1, SPAN1_FLAGS); + assertSpan(charSequencesFromSpanned[1], span2, 0, 1, SPAN2_FLAGS); + + // text: "bcd" + // span1: ^^^ + // span2: ^^ + // span3: + assertEquals("bcd", charSequencesFromString[2].toString()); + assertSpanCount(0, charSequencesFromString[2]); + assertEquals("bcd", charSequencesFromSpanned[2].toString()); + assertSpanCount(2, charSequencesFromSpanned[2]); + assertSpan(charSequencesFromSpanned[2], span1, 0, 3, SPAN1_FLAGS); + assertSpan(charSequencesFromSpanned[2], span2, 0, 2, SPAN2_FLAGS); + + // text: "efg" + // span1: + // span2: + // span3: + assertEquals("efg", charSequencesFromString[3].toString()); + assertSpanCount(0, charSequencesFromString[3]); + assertEquals("efg", charSequencesFromSpanned[3].toString()); + assertSpanCount(0, charSequencesFromSpanned[3]); + + // text: "hij" + // span1: + // span2: + // span3: ^ + assertEquals("hij", charSequencesFromString[4].toString()); + assertSpanCount(0, charSequencesFromString[4]); + assertEquals("hij", charSequencesFromSpanned[4].toString()); + assertSpanCount(1, charSequencesFromSpanned[4]); + assertSpan(charSequencesFromSpanned[4], span3, 1, 2, SPAN3_FLAGS); + + // text: "" + // span1: + // span2: + // span3: + assertEquals("", charSequencesFromString[5].toString()); + assertSpanCount(0, charSequencesFromString[5]); + assertEquals("", charSequencesFromSpanned[5].toString()); + assertSpanCount(0, charSequencesFromSpanned[5]); + + // text: "" + // span1: + // span2: + // span3: + assertEquals("", charSequencesFromString[6].toString()); + assertSpanCount(0, charSequencesFromString[6]); + assertEquals("", charSequencesFromSpanned[6].toString()); + assertSpanCount(0, charSequencesFromSpanned[6]); + } + + public void testSplitCharSequencePreserveTrailingEmptySegmengs() { + assertEquals(1, SpannableStringUtils.split("", " ", + false /* preserveTrailingEmptySegmengs */).length); + assertEquals(1, SpannableStringUtils.split(new SpannedString(""), " ", + false /* preserveTrailingEmptySegmengs */).length); + + assertEquals(1, SpannableStringUtils.split("", " ", + true /* preserveTrailingEmptySegmengs */).length); + assertEquals(1, SpannableStringUtils.split(new SpannedString(""), " ", + true /* preserveTrailingEmptySegmengs */).length); + + assertEquals(0, SpannableStringUtils.split(" ", " ", + false /* preserveTrailingEmptySegmengs */).length); + assertEquals(0, SpannableStringUtils.split(new SpannedString(" "), " ", + false /* preserveTrailingEmptySegmengs */).length); + + assertEquals(2, SpannableStringUtils.split(" ", " ", + true /* preserveTrailingEmptySegmengs */).length); + assertEquals(2, SpannableStringUtils.split(new SpannedString(" "), " ", + true /* preserveTrailingEmptySegmengs */).length); + + assertEquals(3, SpannableStringUtils.split("a b c ", " ", + false /* preserveTrailingEmptySegmengs */).length); + assertEquals(3, SpannableStringUtils.split(new SpannedString("a b c "), " ", + false /* preserveTrailingEmptySegmengs */).length); + + assertEquals(5, SpannableStringUtils.split("a b c ", " ", + true /* preserveTrailingEmptySegmengs */).length); + assertEquals(5, SpannableStringUtils.split(new SpannedString("a b c "), " ", + true /* preserveTrailingEmptySegmengs */).length); + + assertEquals(6, SpannableStringUtils.split("a b ", " ", + false /* preserveTrailingEmptySegmengs */).length); + assertEquals(6, SpannableStringUtils.split(new SpannedString("a b "), " ", + false /* preserveTrailingEmptySegmengs */).length); + + assertEquals(7, SpannableStringUtils.split("a b ", " ", + true /* preserveTrailingEmptySegmengs */).length); + assertEquals(7, SpannableStringUtils.split(new SpannedString("a b "), " ", + true /* preserveTrailingEmptySegmengs */).length); + } } diff --git a/tests/src/com/android/inputmethod/latin/utils/StringUtilsTests.java b/tests/src/com/android/inputmethod/latin/utils/StringUtilsTests.java deleted file mode 100644 index ba2e99802..000000000 --- a/tests/src/com/android/inputmethod/latin/utils/StringUtilsTests.java +++ /dev/null @@ -1,192 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.inputmethod.latin.utils; - -import android.test.AndroidTestCase; -import android.test.suitebuilder.annotation.SmallTest; - -import com.android.inputmethod.latin.Constants; -import com.android.inputmethod.latin.utils.StringUtils; - -import java.util.Locale; - -@SmallTest -public class StringUtilsTests extends AndroidTestCase { - private static final Locale US = Locale.US; - private static final Locale GERMAN = Locale.GERMAN; - private static final Locale TURKEY = new Locale("tr", "TR"); - private static final Locale GREECE = new Locale("el", "GR"); - - private static void assert_toUpperCaseOfStringForLocale(final Locale locale, - final String lowerCase, final String expected) { - assertEquals(lowerCase + " in " + locale, expected, - StringUtils.toUpperCaseOfStringForLocale( - lowerCase, true /* needsToUpperCase */, locale)); - } - - public void test_toUpperCaseOfStringForLocale() { - assert_toUpperCaseOfStringForLocale(US, null, null); - assert_toUpperCaseOfStringForLocale(US, "", ""); - assert_toUpperCaseOfStringForLocale(US, "aeiou", "AEIOU"); - // U+00E0: "à" LATIN SMALL LETTER A WITH GRAVE - // U+00E8: "è" LATIN SMALL LETTER E WITH GRAVE - // U+00EE: "î" LATIN SMALL LETTER I WITH CIRCUMFLEX - // U+00F6: "ö" LATIN SMALL LETTER O WITH DIAERESIS - // U+016B: "ū" LATIN SMALL LETTER U WITH MACRON - // U+00F1: "ñ" LATIN SMALL LETTER N WITH TILDE - // U+00E7: "ç" LATIN SMALL LETTER C WITH CEDILLA - // U+00C0: "À" LATIN CAPITAL LETTER A WITH GRAVE - // U+00C8: "È" LATIN CAPITAL LETTER E WITH GRAVE - // U+00CE: "Î" LATIN CAPITAL LETTER I WITH CIRCUMFLEX - // U+00D6: "Ö" LATIN CAPITAL LETTER O WITH DIAERESIS - // U+016A: "Ū" LATIN CAPITAL LETTER U WITH MACRON - // U+00D1: "Ñ" LATIN CAPITAL LETTER N WITH TILDE - // U+00C7: "Ç" LATIN CAPITAL LETTER C WITH CEDILLA - assert_toUpperCaseOfStringForLocale(US, - "\u00E0\u00E8\u00EE\u00F6\u016B\u00F1\u00E7", - "\u00C0\u00C8\u00CE\u00D6\u016A\u00D1\u00C7"); - // U+00DF: "ß" LATIN SMALL LETTER SHARP S - // U+015B: "ś" LATIN SMALL LETTER S WITH ACUTE - // U+0161: "š" LATIN SMALL LETTER S WITH CARON - // U+015A: "Ś" LATIN CAPITAL LETTER S WITH ACUTE - // U+0160: "Š" LATIN CAPITAL LETTER S WITH CARONZ - assert_toUpperCaseOfStringForLocale(GERMAN, - "\u00DF\u015B\u0161", - "SS\u015A\u0160"); - // U+0259: "ə" LATIN SMALL LETTER SCHWA - // U+0069: "i" LATIN SMALL LETTER I - // U+0131: "ı" LATIN SMALL LETTER DOTLESS I - // U+018F: "Ə" LATIN SMALL LETTER SCHWA - // U+0130: "İ" LATIN SMALL LETTER I WITH DOT ABOVE - // U+0049: "I" LATIN SMALL LETTER I - assert_toUpperCaseOfStringForLocale(TURKEY, - "\u0259\u0069\u0131", - "\u018F\u0130\u0049"); - // U+03C3: "σ" GREEK SMALL LETTER SIGMA - // U+03C2: "ς" GREEK SMALL LETTER FINAL SIGMA - // U+03A3: "Σ" GREEK CAPITAL LETTER SIGMA - assert_toUpperCaseOfStringForLocale(GREECE, - "\u03C3\u03C2", - "\u03A3\u03A3"); - // U+03AC: "ά" GREEK SMALL LETTER ALPHA WITH TONOS - // U+03AD: "έ" GREEK SMALL LETTER EPSILON WITH TONOS - // U+03AE: "ή" GREEK SMALL LETTER ETA WITH TONOS - // U+03AF: "ί" GREEK SMALL LETTER IOTA WITH TONOS - // U+03CC: "ό" GREEK SMALL LETTER OMICRON WITH TONOS - // U+03CD: "ύ" GREEK SMALL LETTER UPSILON WITH TONOS - // U+03CE: "ώ" GREEK SMALL LETTER OMEGA WITH TONOS - // U+0386: "Ά" GREEK CAPITAL LETTER ALPHA WITH TONOS - // U+0388: "Έ" GREEK CAPITAL LETTER EPSILON WITH TONOS - // U+0389: "Ή" GREEK CAPITAL LETTER ETA WITH TONOS - // U+038A: "Ί" GREEK CAPITAL LETTER IOTA WITH TONOS - // U+038C: "Ό" GREEK CAPITAL LETTER OMICRON WITH TONOS - // U+038E: "Ύ" GREEK CAPITAL LETTER UPSILON WITH TONOS - // U+038F: "Ώ" GREEK CAPITAL LETTER OMEGA WITH TONOS - assert_toUpperCaseOfStringForLocale(GREECE, - "\u03AC\u03AD\u03AE\u03AF\u03CC\u03CD\u03CE", - "\u0386\u0388\u0389\u038A\u038C\u038E\u038F"); - // U+03CA: "ϊ" GREEK SMALL LETTER IOTA WITH DIALYTIKA - // U+03CB: "ϋ" GREEK SMALL LETTER UPSILON WITH DIALYTIKA - // U+0390: "ΐ" GREEK SMALL LETTER IOTA WITH DIALYTIKA AND TONOS - // U+03B0: "ΰ" GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND TONOS - // U+03AA: "Ϊ" GREEK CAPITAL LETTER IOTA WITH DIALYTIKA - // U+03AB: "Ϋ" GREEK CAPITAL LETTER UPSILON WITH DIALYTIKA - // U+0399: "Ι" GREEK CAPITAL LETTER IOTA - // U+03A5: "Υ" GREEK CAPITAL LETTER UPSILON - // U+0308: COMBINING DIAERESIS - // U+0301: COMBINING GRAVE ACCENT - assert_toUpperCaseOfStringForLocale(GREECE, - "\u03CA\u03CB\u0390\u03B0", - "\u03AA\u03AB\u0399\u0308\u0301\u03A5\u0308\u0301"); - } - - private static void assert_toUpperCaseOfCodeForLocale(final Locale locale, final int lowerCase, - final int expected) { - assertEquals(lowerCase + " in " + locale, expected, - StringUtils.toUpperCaseOfCodeForLocale( - lowerCase, true /* needsToUpperCase */, locale)); - } - - public void test_toUpperCaseOfCodeForLocale() { - assert_toUpperCaseOfCodeForLocale(US, Constants.CODE_ENTER, Constants.CODE_ENTER); - assert_toUpperCaseOfCodeForLocale(US, Constants.CODE_SPACE, Constants.CODE_SPACE); - assert_toUpperCaseOfCodeForLocale(US, Constants.CODE_COMMA, Constants.CODE_COMMA); - // U+0069: "i" LATIN SMALL LETTER I - // U+0131: "ı" LATIN SMALL LETTER DOTLESS I - // U+0130: "İ" LATIN SMALL LETTER I WITH DOT ABOVE - // U+0049: "I" LATIN SMALL LETTER I - assert_toUpperCaseOfCodeForLocale(US, 0x0069, 0x0049); // i -> I - assert_toUpperCaseOfCodeForLocale(US, 0x0131, 0x0049); // ı -> I - assert_toUpperCaseOfCodeForLocale(TURKEY, 0x0069, 0x0130); // i -> İ - assert_toUpperCaseOfCodeForLocale(TURKEY, 0x0131, 0x0049); // ı -> I - // U+00DF: "ß" LATIN SMALL LETTER SHARP S - // The title case of "ß" is "SS". - assert_toUpperCaseOfCodeForLocale(US, 0x00DF, Constants.CODE_UNSPECIFIED); - // U+03AC: "ά" GREEK SMALL LETTER ALPHA WITH TONOS - // U+0386: "Ά" GREEK CAPITAL LETTER ALPHA WITH TONOS - assert_toUpperCaseOfCodeForLocale(GREECE, 0x03AC, 0x0386); - // U+03CA: "ϊ" GREEK SMALL LETTER IOTA WITH DIALYTIKA - // U+03AA: "Ϊ" GREEK CAPITAL LETTER IOTA WITH DIALYTIKA - assert_toUpperCaseOfCodeForLocale(GREECE, 0x03CA, 0x03AA); - // U+03B0: "ΰ" GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND TONOS - // The title case of "ΰ" is "\u03A5\u0308\u0301". - assert_toUpperCaseOfCodeForLocale(GREECE, 0x03B0, Constants.CODE_UNSPECIFIED); - } - - private static void assert_capitalizeFirstCodePoint(final Locale locale, final String text, - final String expected) { - assertEquals(text + " in " + locale, expected, - StringUtils.capitalizeFirstCodePoint(text, locale)); - } - - public void test_capitalizeFirstCodePoint() { - assert_capitalizeFirstCodePoint(US, "", ""); - assert_capitalizeFirstCodePoint(US, "a", "A"); - assert_capitalizeFirstCodePoint(US, "à", "À"); - assert_capitalizeFirstCodePoint(US, "ß", "SS"); - assert_capitalizeFirstCodePoint(US, "text", "Text"); - assert_capitalizeFirstCodePoint(US, "iGoogle", "IGoogle"); - assert_capitalizeFirstCodePoint(TURKEY, "iyi", "İyi"); - assert_capitalizeFirstCodePoint(TURKEY, "ısırdı", "Isırdı"); - assert_capitalizeFirstCodePoint(GREECE, "ά", "Ά"); - assert_capitalizeFirstCodePoint(GREECE, "άνεση", "Άνεση"); - } - - private static void assert_capitalizeFirstAndDowncaseRest(final Locale locale, - final String text, final String expected) { - assertEquals(text + " in " + locale, expected, - StringUtils.capitalizeFirstAndDowncaseRest(text, locale)); - } - - public void test_capitalizeFirstAndDowncaseRest() { - assert_capitalizeFirstAndDowncaseRest(US, "", ""); - assert_capitalizeFirstAndDowncaseRest(US, "a", "A"); - assert_capitalizeFirstAndDowncaseRest(US, "à", "À"); - assert_capitalizeFirstAndDowncaseRest(US, "ß", "SS"); - assert_capitalizeFirstAndDowncaseRest(US, "text", "Text"); - assert_capitalizeFirstAndDowncaseRest(US, "iGoogle", "Igoogle"); - assert_capitalizeFirstAndDowncaseRest(US, "invite", "Invite"); - assert_capitalizeFirstAndDowncaseRest(US, "INVITE", "Invite"); - assert_capitalizeFirstAndDowncaseRest(TURKEY, "iyi", "İyi"); - assert_capitalizeFirstAndDowncaseRest(TURKEY, "İYİ", "İyi"); - assert_capitalizeFirstAndDowncaseRest(TURKEY, "ısırdı", "Isırdı"); - assert_capitalizeFirstAndDowncaseRest(TURKEY, "ISIRDI", "Isırdı"); - assert_capitalizeFirstAndDowncaseRest(GREECE, "ά", "Ά"); - assert_capitalizeFirstAndDowncaseRest(GREECE, "άνεση", "Άνεση"); - assert_capitalizeFirstAndDowncaseRest(GREECE, "ΆΝΕΣΗ", "Άνεση"); - } -} diff --git a/tests/src/com/android/inputmethod/latin/utils/SubtypeLocaleUtilsTests.java b/tests/src/com/android/inputmethod/latin/utils/SubtypeLocaleUtilsTests.java index ce3df7dd6..2297cacf8 100644 --- a/tests/src/com/android/inputmethod/latin/utils/SubtypeLocaleUtilsTests.java +++ b/tests/src/com/android/inputmethod/latin/utils/SubtypeLocaleUtilsTests.java @@ -23,7 +23,9 @@ import android.test.suitebuilder.annotation.SmallTest; import android.view.inputmethod.InputMethodInfo; import android.view.inputmethod.InputMethodSubtype; +import com.android.inputmethod.latin.R; import com.android.inputmethod.latin.RichInputMethodManager; +import com.android.inputmethod.latin.RichInputMethodSubtype; import java.util.ArrayList; import java.util.Locale; @@ -31,10 +33,11 @@ import java.util.Locale; @SmallTest public class SubtypeLocaleUtilsTests extends AndroidTestCase { // All input method subtypes of LatinIME. - private final ArrayList<InputMethodSubtype> mSubtypesList = new ArrayList<>(); + private final ArrayList<RichInputMethodSubtype> mSubtypesList = new ArrayList<>(); private RichInputMethodManager mRichImm; private Resources mRes; + private InputMethodSubtype mSavedAddtionalSubtypes[]; InputMethodSubtype EN_US; InputMethodSubtype EN_GB; @@ -44,6 +47,8 @@ public class SubtypeLocaleUtilsTests extends AndroidTestCase { InputMethodSubtype FR_CH; InputMethodSubtype DE; InputMethodSubtype DE_CH; + InputMethodSubtype HI; + InputMethodSubtype SR; InputMethodSubtype ZZ; InputMethodSubtype DE_QWERTY; InputMethodSubtype FR_QWERTZ; @@ -53,20 +58,33 @@ public class SubtypeLocaleUtilsTests extends AndroidTestCase { InputMethodSubtype ZZ_AZERTY; InputMethodSubtype ZZ_PC; + // These are preliminary subtypes and may not exist. + InputMethodSubtype HI_LATN; // Hinglish + InputMethodSubtype SR_LATN; // Serbian Latin + InputMethodSubtype HI_LATN_DVORAK; // Hinglis Dvorak + InputMethodSubtype SR_LATN_QWERTY; // Serbian Latin Qwerty + @Override protected void setUp() throws Exception { super.setUp(); final Context context = getContext(); + mRes = context.getResources(); RichInputMethodManager.init(context); mRichImm = RichInputMethodManager.getInstance(); - mRes = context.getResources(); - SubtypeLocaleUtils.init(context); + + // Save and reset additional subtypes + mSavedAddtionalSubtypes = mRichImm.getAdditionalSubtypes(); + final InputMethodSubtype[] predefinedAddtionalSubtypes = + AdditionalSubtypeUtils.createAdditionalSubtypesArray( + AdditionalSubtypeUtils.createPrefSubtypes( + mRes.getStringArray(R.array.predefined_subtypes))); + mRichImm.setAdditionalInputMethodSubtypes(predefinedAddtionalSubtypes); final InputMethodInfo imi = mRichImm.getInputMethodInfoOfThisIme(); final int subtypeCount = imi.getSubtypeCount(); for (int index = 0; index < subtypeCount; index++) { final InputMethodSubtype subtype = imi.getSubtypeAt(index); - mSubtypesList.add(subtype); + mSubtypesList.add(new RichInputMethodSubtype(subtype)); } EN_US = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet( @@ -85,6 +103,10 @@ public class SubtypeLocaleUtilsTests extends AndroidTestCase { Locale.GERMAN.toString(), "qwertz"); DE_CH = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet( "de_CH", "swiss"); + HI = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet( + "hi", "hindi"); + SR = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet( + "sr", "south_slavic"); ZZ = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet( SubtypeLocaleUtils.NO_LANGUAGE, "qwerty"); DE_QWERTY = AdditionalSubtypeUtils.createAsciiEmojiCapableAdditionalSubtype( @@ -101,19 +123,37 @@ public class SubtypeLocaleUtilsTests extends AndroidTestCase { SubtypeLocaleUtils.NO_LANGUAGE, "azerty"); ZZ_PC = AdditionalSubtypeUtils.createAsciiEmojiCapableAdditionalSubtype( SubtypeLocaleUtils.NO_LANGUAGE, "pcqwerty"); + + HI_LATN = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet("hi_ZZ", "qwerty"); + if (HI_LATN != null) { + HI_LATN_DVORAK = AdditionalSubtypeUtils.createAsciiEmojiCapableAdditionalSubtype( + "hi_ZZ", "dvorak"); + } + SR_LATN = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet("sr_ZZ", "serbian_qwertz"); + if (SR_LATN != null) { + SR_LATN_QWERTY = AdditionalSubtypeUtils.createAsciiEmojiCapableAdditionalSubtype( + "sr_ZZ", "qwerty"); + } + } + + @Override + protected void tearDown() throws Exception { + // Restore additional subtypes. + mRichImm.setAdditionalInputMethodSubtypes(mSavedAddtionalSubtypes); + super.tearDown(); } public void testAllFullDisplayName() { - for (final InputMethodSubtype subtype : mSubtypesList) { + for (final RichInputMethodSubtype subtype : mSubtypesList) { final String subtypeName = SubtypeLocaleUtils - .getSubtypeDisplayNameInSystemLocale(subtype); - if (SubtypeLocaleUtils.isNoLanguage(subtype)) { + .getSubtypeDisplayNameInSystemLocale(subtype.getRawSubtype()); + if (subtype.isNoLanguage()) { final String layoutName = SubtypeLocaleUtils - .getKeyboardLayoutSetDisplayName(subtype); + .getKeyboardLayoutSetDisplayName(subtype.getRawSubtype()); assertTrue(subtypeName, subtypeName.contains(layoutName)); } else { final String languageName = SubtypeLocaleUtils - .getSubtypeLocaleDisplayNameInSystemLocale(subtype.getLocale()); + .getSubtypeLocaleDisplayNameInSystemLocale(subtype.getLocale().toString()); assertTrue(subtypeName, subtypeName.contains(languageName)); } } @@ -128,6 +168,8 @@ public class SubtypeLocaleUtilsTests extends AndroidTestCase { assertEquals("fr_CH", "swiss", SubtypeLocaleUtils.getKeyboardLayoutSetName(FR_CH)); assertEquals("de", "qwertz", SubtypeLocaleUtils.getKeyboardLayoutSetName(DE)); assertEquals("de_CH", "swiss", SubtypeLocaleUtils.getKeyboardLayoutSetName(DE_CH)); + assertEquals("hi", "hindi", SubtypeLocaleUtils.getKeyboardLayoutSetName(HI)); + assertEquals("sr", "south_slavic", SubtypeLocaleUtils.getKeyboardLayoutSetName(SR)); assertEquals("zz", "qwerty", SubtypeLocaleUtils.getKeyboardLayoutSetName(ZZ)); assertEquals("de qwerty", "qwerty", SubtypeLocaleUtils.getKeyboardLayoutSetName(DE_QWERTY)); @@ -140,27 +182,46 @@ public class SubtypeLocaleUtilsTests extends AndroidTestCase { SubtypeLocaleUtils.getKeyboardLayoutSetName(ES_US_COLEMAK)); assertEquals("zz azerty", "azerty", SubtypeLocaleUtils.getKeyboardLayoutSetName(ZZ_AZERTY)); + + // These are preliminary subtypes and may not exist. + if (HI_LATN != null) { + assertEquals("hi_ZZ", "qwerty", SubtypeLocaleUtils.getKeyboardLayoutSetName(HI_LATN)); + assertEquals("hi_ZZ dvorak", "dvorak", + SubtypeLocaleUtils.getKeyboardLayoutSetName(HI_LATN_DVORAK)); + } + if (SR_LATN != null) { + assertEquals("sr_ZZ", "serbian_qwertz", + SubtypeLocaleUtils.getKeyboardLayoutSetName(SR_LATN)); + assertEquals("sr_ZZ qwerty", "qwerty", + SubtypeLocaleUtils.getKeyboardLayoutSetName(SR_LATN_QWERTY)); + } } // 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) - // fr_CH swiss F French (Switzerland) - // de qwertz F German - // de_CH swiss F German (Switzerland) - // 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) + // 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) + // fr_CH swiss F French (Switzerland) + // de qwertz F German + // de_CH swiss F German (Switzerland) + // hi hindi F Hindi + // hi_ZZ qwerty F Hinglish exception + // sr south_slavic F Serbian + // sr_ZZ serbian_qwertz F Serbian (Latin) exception + // 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 + // hi_ZZ dvorak T Hinglish (Dvorka) exception + // sr_ZZ qwerty T Serbian (QWERTY) exception + // zz pc T Alphabet (PC) public void testPredefinedSubtypesInEnglishSystemLocale() { final RunInLocale<Void> tests = new RunInLocale<Void>() { @@ -182,8 +243,21 @@ public class SubtypeLocaleUtilsTests extends AndroidTestCase { SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(DE)); assertEquals("de_CH", "German (Switzerland)", SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(DE_CH)); + assertEquals("hi", "Hindi", + SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(HI)); + assertEquals("sr", "Serbian", + SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(SR)); assertEquals("zz", "Alphabet (QWERTY)", SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(ZZ)); + // These are preliminary subtypes and may not exist. + if (HI_LATN != null) { + assertEquals("hi_ZZ", "Hinglish", + SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(HI_LATN)); + } + if (SR_LATN != null) { + assertEquals("sr_ZZ", "Serbian (Latin)", + SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(SR_LATN)); + } return null; } }; @@ -208,6 +282,15 @@ public class SubtypeLocaleUtilsTests extends AndroidTestCase { SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(ZZ_AZERTY)); assertEquals("zz pc", "Alphabet (PC)", SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(ZZ_PC)); + // These are preliminary subtypes and may not exist. + if (HI_LATN_DVORAK != null) { + assertEquals("hi_ZZ", "Hinglish (Dvorak)", + SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(HI_LATN_DVORAK)); + } + if (SR_LATN_QWERTY != null) { + assertEquals("sr_ZZ", "Serbian (QWERTY)", + SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(SR_LATN_QWERTY)); + } return null; } }; @@ -218,21 +301,27 @@ public class SubtypeLocaleUtilsTests extends AndroidTestCase { // 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) - // fr_CH swiss F Français (Suisse) - // de qwertz F Allemand - // de_CH swiss F Allemand (Suisse) - // zz qwerty F Alphabet latin (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 latin (PC) + // 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) + // fr_CH swiss F Français (Suisse) + // de qwertz F Allemand + // de_CH swiss F Allemand (Suisse) + // hi hindi F Hindi exception + // hi_ZZ qwerty F Hindi/Anglais exception + // sr south_slavic F Serbe exception + // sr_ZZ serbian_qwertz F Serbe (latin) exception + // zz qwerty F Alphabet latin (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 + // hi_ZZ dvorak T Hindi/Anglais (Dvorka) exception + // sr_ZZ qwerty T Serbe (QWERTY) exception + // zz pc T Alphabet latin (PC) public void testPredefinedSubtypesInFrenchSystemLocale() { final RunInLocale<Void> tests = new RunInLocale<Void>() { @@ -254,8 +343,21 @@ public class SubtypeLocaleUtilsTests extends AndroidTestCase { SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(DE)); assertEquals("de_CH", "Allemand (Suisse)", SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(DE_CH)); + assertEquals("hi", "Hindi", + SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(HI)); + assertEquals("sr", "Serbe", + SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(SR)); assertEquals("zz", "Alphabet latin (QWERTY)", SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(ZZ)); + // These are preliminary subtypes and may not exist. + if (HI_LATN != null) { + assertEquals("hi_ZZ", "Hindi/Anglais", + SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(HI_LATN)); + } + if (SR_LATN != null) { + assertEquals("sr_ZZ", "Serbe (latin)", + SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(SR_LATN)); + } return null; } }; @@ -280,12 +382,75 @@ public class SubtypeLocaleUtilsTests extends AndroidTestCase { SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(ZZ_AZERTY)); assertEquals("zz pc", "Alphabet latin (PC)", SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(ZZ_PC)); + // These are preliminary subtypes and may not exist. + if (HI_LATN_DVORAK != null) { + assertEquals("hi_ZZ", "Hindi/Anglais (Dvorak)", + SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(HI_LATN_DVORAK)); + } + if (SR_LATN_QWERTY != null) { + assertEquals("sr_ZZ", "Serbe (QWERTY)", + SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(SR_LATN_QWERTY)); + } return null; } }; tests.runInLocale(mRes, Locale.FRENCH); } + // InputMethodSubtype's display name in system locale (hi). + // isAdditionalSubtype (T=true, F=false) + // locale layout | display name + // ------ ------- - ---------------------- + // hi hindi F हिन्दी + // hi_ZZ qwerty F हिंग्लिश + // hi_ZZ dvorak T हिंग्लिश (Dvorak) + + public void testHinglishSubtypesInHindiSystemLocale() { + final RunInLocale<Void> tests = new RunInLocale<Void>() { + @Override + protected Void job (final Resources res) { + assertEquals("hi", "हिन्दी", + SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(HI)); + // These are preliminary subtypes and may not exist. + if (HI_LATN != null) { + assertEquals("hi_ZZ", "हिंग्लिश", + SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(HI_LATN)); + assertEquals("hi_ZZ", "हिंग्लिश (Dvorak)", + SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(HI_LATN_DVORAK)); + } + return null; + } + }; + tests.runInLocale(mRes, new Locale("hi")); + } + + // InputMethodSubtype's display name in system locale (sr). + // isAdditionalSubtype (T=true, F=false) + // locale layout | display name + // ------ -------------- - ---------------------- + // sr south_slavic F Српски + // sr_ZZ serbian_qwertz F Српски (латиница) + // sr_ZZ qwerty T Српски (QWERTY) + + public void testSerbianLatinSubtypesInSerbianSystemLocale() { + final RunInLocale<Void> tests = new RunInLocale<Void>() { + @Override + protected Void job (final Resources res) { + assertEquals("sr", "Српски", + SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(SR)); + // These are preliminary subtypes and may not exist. + if (SR_LATN != null) { + assertEquals("sr_ZZ", "Српски (латиница)", + SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(SR_LATN)); + assertEquals("sr_ZZ", "Српски (QWERTY)", + SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(SR_LATN_QWERTY)); + } + return null; + } + }; + tests.runInLocale(mRes, new Locale("sr")); + } + public void testIsRtlLanguage() { // Known Right-to-Left language subtypes. final InputMethodSubtype ARABIC = mRichImm @@ -298,13 +463,15 @@ public class SubtypeLocaleUtilsTests extends AndroidTestCase { .findSubtypeByLocaleAndKeyboardLayoutSet("iw", "hebrew"); assertNotNull("Hebrew", HEBREW); - for (final InputMethodSubtype subtype : mSubtypesList) { + for (final RichInputMethodSubtype subtype : mSubtypesList) { + final InputMethodSubtype rawSubtype = subtype.getRawSubtype(); final String subtypeName = SubtypeLocaleUtils - .getSubtypeDisplayNameInSystemLocale(subtype); - if (subtype.equals(ARABIC) || subtype.equals(FARSI) || subtype.equals(HEBREW)) { - assertTrue(subtypeName, SubtypeLocaleUtils.isRtlLanguage(subtype)); + .getSubtypeDisplayNameInSystemLocale(rawSubtype); + if (rawSubtype.equals(ARABIC) || rawSubtype.equals(FARSI) + || rawSubtype.equals(HEBREW)) { + assertTrue(subtypeName, subtype.isRtlSubtype()); } else { - assertFalse(subtypeName, SubtypeLocaleUtils.isRtlLanguage(subtype)); + assertFalse(subtypeName, subtype.isRtlSubtype()); } } } |