diff options
Diffstat (limited to 'tests/src/com/android/inputmethod/latin')
34 files changed, 2009 insertions, 583 deletions
diff --git a/tests/src/com/android/inputmethod/latin/BinaryDictionaryDecayingTests.java b/tests/src/com/android/inputmethod/latin/BinaryDictionaryDecayingTests.java index ae184268c..8d5d6ccec 100644 --- a/tests/src/com/android/inputmethod/latin/BinaryDictionaryDecayingTests.java +++ b/tests/src/com/android/inputmethod/latin/BinaryDictionaryDecayingTests.java @@ -20,7 +20,7 @@ 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.NgramContext.WordInfo; import com.android.inputmethod.latin.makedict.BinaryDictIOUtils; import com.android.inputmethod.latin.makedict.CodePointUtils; import com.android.inputmethod.latin.makedict.DictDecoder; @@ -78,13 +78,13 @@ public class BinaryDictionaryDecayingTests extends AndroidTestCase { private 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, mCurrentTime /* timestamp */); } private static boolean isValidBigram(final BinaryDictionary binaryDictionary, final String word0, final String word1) { - return binaryDictionary.isValidNgram(new PrevWordsInfo(new WordInfo(word0)), word1); + return binaryDictionary.isValidNgram(new NgramContext(new WordInfo(word0)), word1); } private void forcePassingShortTime(final BinaryDictionary binaryDictionary) { @@ -342,31 +342,31 @@ public class BinaryDictionaryDecayingTests extends AndroidTestCase { } final int maxUnigramCount = Integer.parseInt( - binaryDictionary.getPropertyForTest(BinaryDictionary.MAX_UNIGRAM_COUNT_QUERY)); + binaryDictionary.getPropertyForGettingStats(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( + Integer.parseInt(binaryDictionary.getPropertyForGettingStats( BinaryDictionary.UNIGRAM_COUNT_QUERY)); while (binaryDictionary.needsToRunGC(true /* mindsBlockByGC */)) { forcePassingShortTime(binaryDictionary); } final int unigramCountAfterGC = - Integer.parseInt(binaryDictionary.getPropertyForTest( + Integer.parseInt(binaryDictionary.getPropertyForGettingStats( BinaryDictionary.UNIGRAM_COUNT_QUERY)); assertTrue(unigramCountBeforeGC > unigramCountAfterGC); } } - assertTrue(Integer.parseInt(binaryDictionary.getPropertyForTest( + assertTrue(Integer.parseInt(binaryDictionary.getPropertyForGettingStats( BinaryDictionary.UNIGRAM_COUNT_QUERY)) > 0); - assertTrue(Integer.parseInt(binaryDictionary.getPropertyForTest( + assertTrue(Integer.parseInt(binaryDictionary.getPropertyForGettingStats( BinaryDictionary.UNIGRAM_COUNT_QUERY)) <= maxUnigramCount); forcePassingLongTime(binaryDictionary); - assertEquals(0, Integer.parseInt(binaryDictionary.getPropertyForTest( + assertEquals(0, Integer.parseInt(binaryDictionary.getPropertyForGettingStats( BinaryDictionary.UNIGRAM_COUNT_QUERY))); } @@ -415,13 +415,13 @@ public class BinaryDictionaryDecayingTests extends AndroidTestCase { } if (binaryDictionary.needsToRunGC(true /* mindsBlockByGC */)) { final int unigramCountBeforeGC = - Integer.parseInt(binaryDictionary.getPropertyForTest( + Integer.parseInt(binaryDictionary.getPropertyForGettingStats( BinaryDictionary.UNIGRAM_COUNT_QUERY)); assertTrue(binaryDictionary.isValidWord(strong)); assertTrue(binaryDictionary.isValidWord(weak)); binaryDictionary.flushWithGC(); final int unigramCountAfterGC = - Integer.parseInt(binaryDictionary.getPropertyForTest( + Integer.parseInt(binaryDictionary.getPropertyForGettingStats( BinaryDictionary.UNIGRAM_COUNT_QUERY)); assertTrue(unigramCountBeforeGC > unigramCountAfterGC); assertFalse(binaryDictionary.isValidWord(weak)); @@ -477,7 +477,7 @@ public class BinaryDictionaryDecayingTests extends AndroidTestCase { } final int maxBigramCount = Integer.parseInt( - binaryDictionary.getPropertyForTest(BinaryDictionary.MAX_BIGRAM_COUNT_QUERY)); + binaryDictionary.getPropertyForGettingStats(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); @@ -486,24 +486,24 @@ public class BinaryDictionaryDecayingTests extends AndroidTestCase { if (binaryDictionary.needsToRunGC(true /* mindsBlockByGC */)) { final int bigramCountBeforeGC = - Integer.parseInt(binaryDictionary.getPropertyForTest( + Integer.parseInt(binaryDictionary.getPropertyForGettingStats( BinaryDictionary.BIGRAM_COUNT_QUERY)); while (binaryDictionary.needsToRunGC(true /* mindsBlockByGC */)) { forcePassingShortTime(binaryDictionary); } final int bigramCountAfterGC = - Integer.parseInt(binaryDictionary.getPropertyForTest( + Integer.parseInt(binaryDictionary.getPropertyForGettingStats( BinaryDictionary.BIGRAM_COUNT_QUERY)); assertTrue(bigramCountBeforeGC > bigramCountAfterGC); } } - assertTrue(Integer.parseInt(binaryDictionary.getPropertyForTest( + assertTrue(Integer.parseInt(binaryDictionary.getPropertyForGettingStats( BinaryDictionary.BIGRAM_COUNT_QUERY)) > 0); - assertTrue(Integer.parseInt(binaryDictionary.getPropertyForTest( + assertTrue(Integer.parseInt(binaryDictionary.getPropertyForGettingStats( BinaryDictionary.BIGRAM_COUNT_QUERY)) <= maxBigramCount); forcePassingLongTime(binaryDictionary); - assertEquals(0, Integer.parseInt(binaryDictionary.getPropertyForTest( + assertEquals(0, Integer.parseInt(binaryDictionary.getPropertyForGettingStats( BinaryDictionary.BIGRAM_COUNT_QUERY))); } @@ -574,11 +574,11 @@ public class BinaryDictionaryDecayingTests extends AndroidTestCase { } if (binaryDictionary.needsToRunGC(true /* mindsBlockByGC */)) { final int bigramCountBeforeGC = - Integer.parseInt(binaryDictionary.getPropertyForTest( + Integer.parseInt(binaryDictionary.getPropertyForGettingStats( BinaryDictionary.BIGRAM_COUNT_QUERY)); binaryDictionary.flushWithGC(); final int bigramCountAfterGC = - Integer.parseInt(binaryDictionary.getPropertyForTest( + Integer.parseInt(binaryDictionary.getPropertyForGettingStats( BinaryDictionary.BIGRAM_COUNT_QUERY)); assertTrue(bigramCountBeforeGC > bigramCountAfterGC); assertTrue(isValidBigram(binaryDictionary, strong, target)); @@ -661,31 +661,63 @@ public class BinaryDictionaryDecayingTests extends AndroidTestCase { BinaryDictionary.NOT_A_PROBABILITY /* shortcutProbability */, true /* isBeginningOfSentence */, true /* isNotAWord */, false /* isBlacklisted */, mCurrentTime); - final PrevWordsInfo prevWordsInfoStartOfSentence = PrevWordsInfo.BEGINNING_OF_SENTENCE; + final NgramContext beginningOfSentenceContext = NgramContext.BEGINNING_OF_SENTENCE; addUnigramWord(binaryDictionary, "aaa", DUMMY_PROBABILITY); - binaryDictionary.addNgramEntry(prevWordsInfoStartOfSentence, "aaa", DUMMY_PROBABILITY, + binaryDictionary.addNgramEntry(beginningOfSentenceContext, "aaa", DUMMY_PROBABILITY, mCurrentTime); - assertTrue(binaryDictionary.isValidNgram(prevWordsInfoStartOfSentence, "aaa")); - binaryDictionary.addNgramEntry(prevWordsInfoStartOfSentence, "aaa", DUMMY_PROBABILITY, + assertTrue(binaryDictionary.isValidNgram(beginningOfSentenceContext, "aaa")); + binaryDictionary.addNgramEntry(beginningOfSentenceContext, "aaa", DUMMY_PROBABILITY, mCurrentTime); addUnigramWord(binaryDictionary, "bbb", DUMMY_PROBABILITY); - binaryDictionary.addNgramEntry(prevWordsInfoStartOfSentence, "bbb", DUMMY_PROBABILITY, + binaryDictionary.addNgramEntry(beginningOfSentenceContext, "bbb", DUMMY_PROBABILITY, mCurrentTime); - assertTrue(binaryDictionary.isValidNgram(prevWordsInfoStartOfSentence, "aaa")); - assertTrue(binaryDictionary.isValidNgram(prevWordsInfoStartOfSentence, "bbb")); + assertTrue(binaryDictionary.isValidNgram(beginningOfSentenceContext, "aaa")); + assertTrue(binaryDictionary.isValidNgram(beginningOfSentenceContext, "bbb")); forcePassingLongTime(binaryDictionary); - assertFalse(binaryDictionary.isValidNgram(prevWordsInfoStartOfSentence, "aaa")); - assertFalse(binaryDictionary.isValidNgram(prevWordsInfoStartOfSentence, "bbb")); + assertFalse(binaryDictionary.isValidNgram(beginningOfSentenceContext, "aaa")); + assertFalse(binaryDictionary.isValidNgram(beginningOfSentenceContext, "bbb")); addUnigramWord(binaryDictionary, "aaa", DUMMY_PROBABILITY); - binaryDictionary.addNgramEntry(prevWordsInfoStartOfSentence, "aaa", DUMMY_PROBABILITY, + binaryDictionary.addNgramEntry(beginningOfSentenceContext, "aaa", DUMMY_PROBABILITY, mCurrentTime); addUnigramWord(binaryDictionary, "bbb", DUMMY_PROBABILITY); - binaryDictionary.addNgramEntry(prevWordsInfoStartOfSentence, "bbb", DUMMY_PROBABILITY, + binaryDictionary.addNgramEntry(beginningOfSentenceContext, "bbb", DUMMY_PROBABILITY, mCurrentTime); - assertTrue(binaryDictionary.isValidNgram(prevWordsInfoStartOfSentence, "aaa")); - assertTrue(binaryDictionary.isValidNgram(prevWordsInfoStartOfSentence, "bbb")); + assertTrue(binaryDictionary.isValidNgram(beginningOfSentenceContext, "aaa")); + assertTrue(binaryDictionary.isValidNgram(beginningOfSentenceContext, "bbb")); + binaryDictionary.close(); + dictFile.delete(); + } + + public void testRemoveUnigrams() { + for (final int formatVersion : DICT_FORMAT_VERSIONS) { + testRemoveUnigrams(formatVersion); + } + } + + private void testRemoveUnigrams(final int formatVersion) { + final int unigramInputCount = 20; + 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 */); + + addUnigramWord(binaryDictionary, "aaa", Dictionary.NOT_A_PROBABILITY); + assertFalse(binaryDictionary.isValidWord("aaa")); + for (int i = 0; i < unigramInputCount; i++) { + addUnigramWord(binaryDictionary, "aaa", Dictionary.NOT_A_PROBABILITY); + } + assertTrue(binaryDictionary.isValidWord("aaa")); + assertTrue(binaryDictionary.removeUnigramEntry("aaa")); + assertFalse(binaryDictionary.isValidWord("aaa")); + 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..9c7792cf2 100644 --- a/tests/src/com/android/inputmethod/latin/BinaryDictionaryTests.java +++ b/tests/src/com/android/inputmethod/latin/BinaryDictionaryTests.java @@ -21,7 +21,7 @@ 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.NgramContext.WordInfo; import com.android.inputmethod.latin.makedict.CodePointUtils; import com.android.inputmethod.latin.makedict.FormatSpec; import com.android.inputmethod.latin.makedict.WeightedString; @@ -55,6 +55,10 @@ public class BinaryDictionaryTests extends AndroidTestCase { return formatVersion > FormatSpec.VERSION401; } + private static boolean supportsNgram(final int formatVersion) { + return formatVersion >= FormatSpec.VERSION4_DEV; + } + private File createEmptyDictionaryAndGetFile(final String dictId, final int formatVersion) throws IOException { if (formatVersion == FormatSpec.VERSION4 @@ -204,23 +208,45 @@ public class BinaryDictionaryTests extends AndroidTestCase { 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 void addTrigramEntry(final BinaryDictionary binaryDictionary, final String word0, + final String word1, final String word2, final int probability) { + final NgramContext ngramContext = + new NgramContext(new WordInfo[] { new WordInfo(word1), new WordInfo(word0) } ); + binaryDictionary.addNgramEntry(ngramContext, word2, 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); + return binaryDictionary.isValidNgram(new NgramContext(new WordInfo(word0)), word1); } private static void removeBigramEntry(final BinaryDictionary binaryDictionary, final String word0, final String word1) { - binaryDictionary.removeNgramEntry(new PrevWordsInfo(new WordInfo(word0)), word1); + binaryDictionary.removeNgramEntry(new NgramContext(new WordInfo(word0)), word1); + } + + private static void removeTrigramEntry(final BinaryDictionary binaryDictionary, + final String word0, final String word1, final String word2) { + final NgramContext ngramContext = + new NgramContext(new WordInfo[] { new WordInfo(word1), new WordInfo(word0) } ); + binaryDictionary.removeNgramEntry(ngramContext, word2); } 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); + } + + private static int getTrigramProbability(final BinaryDictionary binaryDictionary, + final String word0, final String word1, final String word2) { + final NgramContext ngramContext = + new NgramContext(new WordInfo[] { new WordInfo(word1), new WordInfo(word0) } ); + return binaryDictionary.getNgramProbability(ngramContext, word2); } public void testAddUnigramWord() { @@ -500,6 +526,56 @@ public class BinaryDictionaryTests extends AndroidTestCase { dictFile.delete(); } + public void testAddTrigramWords() { + for (final int formatVersion : DICT_FORMAT_VERSIONS) { + if (supportsNgram(formatVersion)) { + testAddTrigramWords(formatVersion); + } + } + } + + private void testAddTrigramWords(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 int unigramProbability = 100; + final int trigramProbability = 150; + final int updatedTrigramProbability = 200; + addUnigramWord(binaryDictionary, "aaa", unigramProbability); + addUnigramWord(binaryDictionary, "abb", unigramProbability); + addUnigramWord(binaryDictionary, "bcc", unigramProbability); + + addBigramWords(binaryDictionary, "abb", "bcc", 10); + addBigramWords(binaryDictionary, "abb", "aaa", 10); + + addTrigramEntry(binaryDictionary, "aaa", "abb", "bcc", trigramProbability); + addTrigramEntry(binaryDictionary, "bcc", "abb", "aaa", trigramProbability); + + assertEquals(trigramProbability, + getTrigramProbability(binaryDictionary, "aaa", "abb", "bcc")); + assertEquals(trigramProbability, + getTrigramProbability(binaryDictionary, "bcc", "abb", "aaa")); + assertFalse(isValidBigram(binaryDictionary, "aaa", "abb")); + + addTrigramEntry(binaryDictionary, "bcc", "abb", "aaa", updatedTrigramProbability); + assertEquals(updatedTrigramProbability, + getTrigramProbability(binaryDictionary, "bcc", "abb", "aaa")); + + removeTrigramEntry(binaryDictionary, "aaa", "abb", "bcc"); + assertEquals(Dictionary.NOT_A_PROBABILITY, + getTrigramProbability(binaryDictionary, "aaa", "abb", "bcc")); + assertTrue(isValidBigram(binaryDictionary, "abb", "bcc")); + + dictFile.delete(); + } + public void testFlushDictionary() { for (final int formatVersion : DICT_FORMAT_VERSIONS) { testFlushDictionary(formatVersion); @@ -892,14 +968,18 @@ 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(); } @@ -1025,7 +1105,7 @@ public class BinaryDictionaryTests extends AndroidTestCase { assertTrue(wordProperty.isValid()); assertEquals(isNotAWord, wordProperty.mIsNotAWord); assertEquals(isBlacklisted, wordProperty.mIsBlacklistEntry); - assertEquals(false, wordProperty.mHasBigrams); + assertEquals(false, wordProperty.mHasNgrams); assertEquals(false, wordProperty.mHasShortcuts); assertEquals(unigramProbability, wordProperty.mProbabilityInfo.mProbability); assertTrue(wordProperty.mShortcutTargets.isEmpty()); @@ -1062,13 +1142,14 @@ 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()); + assertEquals(bigramProbability, bigramTarget.getProbability()); } } } @@ -1155,13 +1236,14 @@ 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; + // TODO: Support ngram. + for (final WeightedString bigramTarget : wordProperty.getBigrams()) { + final String word1 = bigramTarget.mWord; assertTrue(bigramWord1s.contains(word1)); final Pair<String, String> bigram = new Pair<>(word0, word1); if (canCheckBigramProbability(formatVersion)) { final int bigramProbability = bigramProbabilitiesToCheckLater.get(bigram); - assertEquals(bigramProbability, wordProperty.mBigrams.get(j).getProbability()); + assertEquals(bigramProbability, bigramTarget.getProbability()); } bigramSet.remove(bigram); } @@ -1342,7 +1424,7 @@ public class BinaryDictionaryTests extends AndroidTestCase { binaryDictionary.addUnigramEntry("ddd", unigramProbability, null /* shortcutTarget */, Dictionary.NOT_A_PROBABILITY, false /* isBeginningOfSentence */, true /* isNotAWord */, true /* isBlacklisted */, 0 /* timestamp */); - binaryDictionary.addNgramEntry(PrevWordsInfo.BEGINNING_OF_SENTENCE, + binaryDictionary.addNgramEntry(NgramContext.BEGINNING_OF_SENTENCE, "aaa", bigramProbability, 0 /* timestamp */); assertEquals(unigramProbability, binaryDictionary.getFrequency("aaa")); assertEquals(unigramProbability, binaryDictionary.getFrequency("bbb")); @@ -1356,7 +1438,7 @@ public class BinaryDictionaryTests extends AndroidTestCase { if (canCheckBigramProbability(toFormatVersion)) { assertEquals(bigramProbability, getBigramProbability(binaryDictionary, "aaa", "bbb")); assertEquals(bigramProbability, binaryDictionary.getNgramProbability( - PrevWordsInfo.BEGINNING_OF_SENTENCE, "aaa")); + NgramContext.BEGINNING_OF_SENTENCE, "aaa")); } assertTrue(isValidBigram(binaryDictionary, "aaa", "bbb")); WordProperty wordProperty = binaryDictionary.getWordProperty("ccc", @@ -1434,7 +1516,7 @@ public class BinaryDictionaryTests extends AndroidTestCase { assertEquals((int)unigramProbabilities.get(word), binaryDictionary.getFrequency(word)); } assertEquals(unigramProbabilities.size(), Integer.parseInt( - binaryDictionary.getPropertyForTest(BinaryDictionary.UNIGRAM_COUNT_QUERY))); + binaryDictionary.getPropertyForGettingStats(BinaryDictionary.UNIGRAM_COUNT_QUERY))); for (final Pair<String, String> bigram : bigrams) { if (canCheckBigramProbability(toFormatVersion)) { @@ -1444,7 +1526,7 @@ public class BinaryDictionaryTests extends AndroidTestCase { assertTrue(isValidBigram(binaryDictionary, bigram.first, bigram.second)); } assertEquals(bigramProbabilities.size(), Integer.parseInt( - binaryDictionary.getPropertyForTest(BinaryDictionary.BIGRAM_COUNT_QUERY))); + binaryDictionary.getPropertyForGettingStats(BinaryDictionary.BIGRAM_COUNT_QUERY))); } public void testBeginningOfSentence() { @@ -1466,23 +1548,23 @@ public class BinaryDictionaryTests extends AndroidTestCase { 0 /* offset */, dictFile.length(), true /* useFullEditDistance */, Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */); 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")); + binaryDictionary.getNgramProbability(beginningOfSentenceContext, "bbb")); } public void testGetMaxFrequencyOfExactMatches() { diff --git a/tests/src/com/android/inputmethod/latin/BlueUnderlineTests.java b/tests/src/com/android/inputmethod/latin/BlueUnderlineTests.java index 6e894decf..30b088137 100644 --- a/tests/src/com/android/inputmethod/latin/BlueUnderlineTests.java +++ b/tests/src/com/android/inputmethod/latin/BlueUnderlineTests.java @@ -63,7 +63,7 @@ public class BlueUnderlineTests extends InputTestsBase { 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); runMessages(); 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..5af30e5b0 --- /dev/null +++ b/tests/src/com/android/inputmethod/latin/DictionaryFacilitatorLruCacheTests.java @@ -0,0 +1,81 @@ +/* + * 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 { + static final int MAX_CACHE_SIZE = 2; + static final int MAX_CACHE_SIZE_LARGE = 5; + + public void testCacheSize() { + final DictionaryFacilitatorLruCache cache = + new DictionaryFacilitatorLruCache(getContext(), MAX_CACHE_SIZE, ""); + + assertEquals(0, cache.getCachedLocalesForTesting().size()); + assertNotNull(cache.get(Locale.US)); + assertEquals(1, cache.getCachedLocalesForTesting().size()); + assertNotNull(cache.get(Locale.UK)); + assertEquals(2, cache.getCachedLocalesForTesting().size()); + assertNotNull(cache.get(Locale.FRENCH)); + assertEquals(2, cache.getCachedLocalesForTesting().size()); + cache.evictAll(); + assertEquals(0, cache.getCachedLocalesForTesting().size()); + } + + public void testGetFacilitator() { + testGetFacilitator(new DictionaryFacilitatorLruCache(getContext(), MAX_CACHE_SIZE, "")); + testGetFacilitator(new DictionaryFacilitatorLruCache( + getContext(), MAX_CACHE_SIZE_LARGE, "")); + } + + private void testGetFacilitator(final DictionaryFacilitatorLruCache cache) { + final DictionaryFacilitator dictionaryFacilitatorEnUs = cache.get(Locale.US); + assertNotNull(dictionaryFacilitatorEnUs); + assertEquals(Locale.US, dictionaryFacilitatorEnUs.getLocale()); + + final DictionaryFacilitator dictionaryFacilitatorFr = cache.get(Locale.FRENCH); + assertNotNull(dictionaryFacilitatorEnUs); + assertEquals(Locale.FRENCH, dictionaryFacilitatorFr.getLocale()); + + final DictionaryFacilitator dictionaryFacilitatorDe = cache.get(Locale.GERMANY); + assertNotNull(dictionaryFacilitatorDe); + assertEquals(Locale.GERMANY, dictionaryFacilitatorDe.getLocale()); + } + + public void testSetUseContactsDictionary() { + testSetUseContactsDictionary(new DictionaryFacilitatorLruCache( + getContext(), MAX_CACHE_SIZE, "")); + testSetUseContactsDictionary(new DictionaryFacilitatorLruCache( + getContext(), MAX_CACHE_SIZE_LARGE, "")); + } + + private void testSetUseContactsDictionary(final DictionaryFacilitatorLruCache cache) { + assertNull(cache.get(Locale.US).getSubDictForTesting(Dictionary.TYPE_CONTACTS)); + cache.setUseContactsDictionary(true /* useContactsDictionary */); + assertNotNull(cache.get(Locale.US).getSubDictForTesting(Dictionary.TYPE_CONTACTS)); + assertNotNull(cache.get(Locale.FRENCH).getSubDictForTesting(Dictionary.TYPE_CONTACTS)); + assertNotNull(cache.get(Locale.GERMANY).getSubDictForTesting(Dictionary.TYPE_CONTACTS)); + cache.setUseContactsDictionary(false /* useContactsDictionary */); + assertNull(cache.get(Locale.GERMANY).getSubDictForTesting(Dictionary.TYPE_CONTACTS)); + assertNull(cache.get(Locale.US).getSubDictForTesting(Dictionary.TYPE_CONTACTS)); + } +} diff --git a/tests/src/com/android/inputmethod/latin/InputLogicTests.java b/tests/src/com/android/inputmethod/latin/InputLogicTests.java index 59b858dbd..ec249dab3 100644 --- a/tests/src/com/android/inputmethod/latin/InputLogicTests.java +++ b/tests/src/com/android/inputmethod/latin/InputLogicTests.java @@ -159,8 +159,11 @@ public class InputLogicTests extends InputTestsBase { } 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); type(Constants.CODE_DELETE); @@ -168,6 +171,24 @@ public class InputLogicTests extends InputTestsBase { 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); + mLatinIME.onUpdateSelection(0, 0, + STRING_TO_TYPE_FIRST.length(), STRING_TO_TYPE_FIRST.length(), -1, -1); + type(Constants.CODE_DELETE); + + type(STRING_TO_TYPE_SECOND); + mLatinIME.onUpdateSelection(STRING_TO_TYPE_FIRST.length(), STRING_TO_TYPE_FIRST.length(), + STRING_TO_TYPE_FIRST.length() - 1 + STRING_TO_TYPE_SECOND.length(), + STRING_TO_TYPE_FIRST.length() - 1 + STRING_TO_TYPE_SECOND.length(), + -1, -1); + 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"; 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..afe7dbe70 --- /dev/null +++ b/tests/src/com/android/inputmethod/latin/InputLogicTestsDeadKeys.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; + +import android.test.suitebuilder.annotation.LargeTest; + +import com.android.inputmethod.event.Event; + +import java.util.ArrayList; + +@LargeTest +public class InputLogicTestsDeadKeys extends InputTestsBase { + // A helper class for readability + private 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/InputLogicTestsNonEnglish.java b/tests/src/com/android/inputmethod/latin/InputLogicTestsNonEnglish.java index 866f8894c..715d449a0 100644 --- a/tests/src/com/android/inputmethod/latin/InputLogicTestsNonEnglish.java +++ b/tests/src/com/android/inputmethod/latin/InputLogicTestsNonEnglish.java @@ -18,6 +18,8 @@ package com.android.inputmethod.latin; import android.test.suitebuilder.annotation.LargeTest; +import com.android.inputmethod.latin.settings.Settings; + @LargeTest public class InputLogicTestsNonEnglish extends InputTestsBase { final String NEXT_WORD_PREDICTION_OPTION = "next_word_prediction"; @@ -121,4 +123,32 @@ public class InputLogicTestsNonEnglish extends InputTestsBase { assertEquals("auto-correct with umlaut for German", EXPECTED_RESULT, mEditText.getText().toString()); } + + // Corresponds to InputLogicTests#testDoubleSpace + public void testDoubleSpaceHindi() { + changeLanguage("hi"); + // 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) { + mEditText.setText(""); + type(STRINGS_TO_TYPE[i]); + assertEquals("double space processing", EXPECTED_RESULTS[i], + mEditText.getText().toString()); + } + } + + // Corresponds to InputLogicTests#testCancelDoubleSpace + public void testCancelDoubleSpaceHindi() { + changeLanguage("hi"); + 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()); + } } diff --git a/tests/src/com/android/inputmethod/latin/InputTestsBase.java b/tests/src/com/android/inputmethod/latin/InputTestsBase.java index 986fb1097..6860bea45 100644 --- a/tests/src/com/android/inputmethod/latin/InputTestsBase.java +++ b/tests/src/com/android/inputmethod/latin/InputTestsBase.java @@ -36,6 +36,7 @@ import android.widget.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.SuggestedWords.SuggestedWordInfo; @@ -182,6 +183,9 @@ public class InputTestsBase extends ServiceTestCase<LatinIMEForTests> { | InputType.TYPE_TEXT_FLAG_MULTI_LINE; mEditText.setInputType(inputType); mEditText.setEnabled(true); + if (null == Looper.myLooper()) { + Looper.prepare(); + } setupService(); mLatinIME = getService(); setDebugMode(true); @@ -263,14 +267,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 = mLatinIME.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 */); } 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..ecc2c634d --- /dev/null +++ b/tests/src/com/android/inputmethod/latin/NgramContextTests.java @@ -0,0 +1,66 @@ +/* + * 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 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), + new NgramContext(WordInfo.BEGINNING_OF_SENTENCE)); + 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) + .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, new WordInfo("a")) + .isBeginningOfSentenceContext()); + assertFalse(new NgramContext(new WordInfo("a"), WordInfo.BEGINNING_OF_SENTENCE) + .isBeginningOfSentenceContext()); + assertFalse(new NgramContext(WordInfo.EMPTY_WORD_INFO, WordInfo.BEGINNING_OF_SENTENCE) + .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); + 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)); + } +} diff --git a/tests/src/com/android/inputmethod/latin/RichInputConnectionAndTextRangeTests.java b/tests/src/com/android/inputmethod/latin/RichInputConnectionAndTextRangeTests.java index f9d72269e..7a3233625 100644 --- a/tests/src/com/android/inputmethod/latin/RichInputConnectionAndTextRangeTests.java +++ b/tests/src/com/android/inputmethod/latin/RichInputConnectionAndTextRangeTests.java @@ -30,9 +30,8 @@ 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.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; @@ -158,26 +157,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).isNthPrevWordBeginningOfSontence(2)); // The following tests reflect the current behavior of the function // RichInputConnection#getNthPreviousWord. @@ -186,33 +184,33 @@ 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); + assertEquals(NgramContextUtils.getNgramContextFromNthPreviousWord( + "abc? def", mSpacingAndPunctuations, 2), NgramContext.EMPTY_PREV_WORDS_INFO); + assertEquals(NgramContextUtils.getNgramContextFromNthPreviousWord( + "abc! def", mSpacingAndPunctuations, 2), NgramContext.EMPTY_PREV_WORDS_INFO); + assertEquals(NgramContextUtils.getNgramContextFromNthPreviousWord( + "abc 'def", mSpacingAndPunctuations, 2), NgramContext.EMPTY_PREV_WORDS_INFO); } public void testGetWordRangeAtCursor() { diff --git a/tests/src/com/android/inputmethod/latin/SuggestedWordsTests.java b/tests/src/com/android/inputmethod/latin/SuggestedWordsTests.java index 869c550e0..563261f8f 100644 --- a/tests/src/com/android/inputmethod/latin/SuggestedWordsTests.java +++ b/tests/src/com/android/inputmethod/latin/SuggestedWordsTests.java @@ -147,7 +147,7 @@ public class SuggestedWordsTests extends AndroidTestCase { 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 */, @@ -157,6 +157,6 @@ public class SuggestedWordsTests extends AndroidTestCase { SuggestedWords.INPUT_STYLE_NONE); assertNull(emptySuggestedWords.getTypedWordInfoOrNull()); - assertNull(SuggestedWords.EMPTY.getTypedWordInfoOrNull()); + assertNull(SuggestedWords.getEmptyInstance().getTypedWordInfoOrNull()); } } 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..00857e54e --- /dev/null +++ b/tests/src/com/android/inputmethod/latin/accounts/AccountsChangedReceiverTests.java @@ -0,0 +1,111 @@ +/* + * 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.Settings; + +/** + * 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(Settings.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(Settings.PREF_ACCOUNT_NAME).apply(); + } else { + mPrefs.edit().putString(Settings.PREF_ACCOUNT_NAME, accountName).apply(); + } + } + + private void assertAccountName(String expectedAccountName) { + assertEquals(expectedAccountName, mPrefs.getString(Settings.PREF_ACCOUNT_NAME, null)); + } +} diff --git a/tests/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderEncoderTests.java b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderEncoderTests.java index 406046a74..a96b3fd0a 100644 --- a/tests/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderEncoderTests.java +++ b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderEncoderTests.java @@ -312,7 +312,7 @@ public class BinaryDictDecoderEncoderTests extends AndroidTestCase { final DictBuffer dictBuffer = new ByteArrayDictBuffer(buffer); for (final String word : sWords) { Arrays.fill(buffer, (byte) 0); - CharEncoding.writeString(buffer, 0, word); + CharEncoding.writeString(buffer, 0, word, null); dictBuffer.position(0); final String str = CharEncoding.readString(dictBuffer); assertEquals(word, str); @@ -682,8 +682,8 @@ public class BinaryDictDecoderEncoderTests extends AndroidTestCase { } assertTrue(shortcutList.isEmpty()); } - for (int j = 0; j < wordProperty.mBigrams.size(); j++) { - final String word1 = wordProperty.mBigrams.get(j).mWord; + for (final WeightedString bigramTarget : wordProperty.getBigrams()) { + final String word1 = bigramTarget.mWord; final Pair<String, String> bigram = new Pair<>(word0, word1); assertTrue(bigramSet.contains(bigram)); bigramSet.remove(bigram); diff --git a/tests/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderUtils.java b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderUtils.java index 96604a197..1f3ee19af 100644 --- a/tests/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderUtils.java +++ b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderUtils.java @@ -17,11 +17,11 @@ package com.android.inputmethod.latin.makedict; import com.android.inputmethod.annotations.UsedForTesting; - import java.io.File; import java.io.IOException; import java.io.OutputStream; import java.nio.ByteBuffer; +import java.util.HashMap; /** * Decodes binary files for a FusionDictionary. @@ -109,15 +109,19 @@ 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(int character, + final HashMap<Integer, Integer> codePointToOneByteCodeMap) { + if (codePointToOneByteCodeMap != null) { + if (codePointToOneByteCodeMap.containsKey(character)) { + character = codePointToOneByteCodeMap.get(character); + } + } + return character >= FormatSpec.MINIMAL_ONE_BYTE_CHARACTER_VALUE + && character <= FormatSpec.MAXIMAL_ONE_BYTE_CHARACTER_VALUE; } /** @@ -137,9 +141,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 +152,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; } @@ -159,11 +165,19 @@ 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 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, int index, + final HashMap<Integer, Integer> codePointToOneByteCodeMap) { 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 +198,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 +231,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 +275,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; } diff --git a/tests/src/com/android/inputmethod/latin/makedict/BinaryDictEncoderUtils.java b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictEncoderUtils.java index eabd8d722..2d536d822 100644 --- a/tests/src/com/android/inputmethod/latin/makedict/BinaryDictEncoderUtils.java +++ b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictEncoderUtils.java @@ -27,6 +27,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 +61,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 +77,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); } /** @@ -90,13 +94,14 @@ public class BinaryDictEncoderUtils { /** * Compute the size of a shortcut in bytes. */ - private static int getShortcutSize(final WeightedString shortcut) { + private static int getShortcutSize(final WeightedString shortcut, + final HashMap<Integer, Integer> codePointToOneByteCodeMap) { 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 += CharEncoding.getCharSize(codePoint, codePointToOneByteCodeMap); } size += FormatSpec.PTNODE_TERMINATOR_SIZE; return size; @@ -108,11 +113,12 @@ public class BinaryDictEncoderUtils { * 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) { + static int getShortcutListSize(final ArrayList<WeightedString> shortcutList, + final HashMap<Integer, Integer> codePointToOneByteCodeMap) { if (null == shortcutList || shortcutList.isEmpty()) return 0; int size = FormatSpec.PTNODE_SHORTCUT_LIST_SIZE_SIZE; for (final WeightedString shortcut : shortcutList) { - size += getShortcutSize(shortcut); + size += getShortcutSize(shortcut, codePointToOneByteCodeMap); } return size; } @@ -123,14 +129,16 @@ public class BinaryDictEncoderUtils { * @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); + // TODO: Use codePointToOneByteCodeMap for shortcuts. + size += getShortcutListSize(ptNode.mShortcutTargets, null /* codePointToOneByteCodeMap */); if (null != ptNode.mBigrams) { size += (FormatSpec.PTNODE_ATTRIBUTE_FLAGS_SIZE + FormatSpec.PTNODE_ATTRIBUTE_MAX_ADDRESS_SIZE) @@ -146,10 +154,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 +170,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); } /** @@ -365,7 +376,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 +385,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 +393,9 @@ public class BinaryDictEncoderUtils { nodeSize += getByteSize(getOffsetToTargetNodeArrayDuringUpdate(ptNodeArray, nodeSize + size, ptNode.mChildren)); } - nodeSize += getShortcutListSize(ptNode.mShortcutTargets); + // TODO: Use codePointToOneByteCodeMap for shortcuts. + nodeSize += getShortcutListSize(ptNode.mShortcutTargets, + null /* codePointToOneByteCodeMap */); if (null != ptNode.mBigrams) { for (WeightedString bigram : ptNode.mBigrams) { final int offset = getOffsetToTargetPtNodeDuringUpdate(ptNodeArray, @@ -452,10 +466,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 +485,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 ?!"); @@ -548,28 +564,6 @@ 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. @@ -706,9 +700,10 @@ public class BinaryDictEncoderUtils { + (frequency & FormatSpec.FLAG_BIGRAM_SHORTCUT_ATTR_FREQUENCY); } - /* package */ static final int getChildrenPosition(final PtNode ptNode) { + /* package */ static final 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 +720,17 @@ 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 +744,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,10 +810,12 @@ 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 @@ -856,8 +851,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 +873,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/BinaryDictUtils.java b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictUtils.java index 5a3eba801..965479d73 100644 --- a/tests/src/com/android/inputmethod/latin/makedict/BinaryDictUtils.java +++ b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictUtils.java @@ -69,7 +69,7 @@ public class BinaryDictUtils { } return new Ver4DictEncoder(file); } else if (formatOptions.mVersion == FormatSpec.VERSION2) { - return new Ver2DictEncoder(file); + return new Ver2DictEncoder(file, Ver2DictEncoder.CODE_POINT_TABLE_OFF); } else { throw new RuntimeException("The format option has a wrong version : " + formatOptions.mVersion); 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/Ver2DictDecoder.java b/tests/src/com/android/inputmethod/latin/makedict/Ver2DictDecoder.java index 65b84d5f7..6227f13e1 100644 --- a/tests/src/com/android/inputmethod/latin/makedict/Ver2DictDecoder.java +++ b/tests/src/com/android/inputmethod/latin/makedict/Ver2DictDecoder.java @@ -177,7 +177,8 @@ public class Ver2DictDecoder extends AbstractDictDecoder { if (header == null) { throw new IOException("Cannot read the dictionary header."); } - if (header.mFormatOptions.mVersion != FormatSpec.VERSION2) { + if (header.mFormatOptions.mVersion != FormatSpec.VERSION2 && + header.mFormatOptions.mVersion != FormatSpec.VERSION201) { throw new UnsupportedFormatException("File header has a wrong version : " + header.mFormatOptions.mVersion); } @@ -200,19 +201,19 @@ public class Ver2DictDecoder extends AbstractDictDecoder { if (0 != (flags & FormatSpec.FLAG_HAS_MULTIPLE_CHARS)) { int index = 0; int character = CharEncoding.readChar(mDictBuffer); - addressPointer += CharEncoding.getCharSize(character); + addressPointer += CharEncoding.getCharSize(character, null); 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); + addressPointer += CharEncoding.getCharSize(character, null); } characters = Arrays.copyOfRange(mCharacterBuffer, 0, index); } else { final int character = CharEncoding.readChar(mDictBuffer); - addressPointer += CharEncoding.getCharSize(character); + addressPointer += CharEncoding.getCharSize(character, null); characters = new int[] { character }; } final ProbabilityInfo probabilityInfo; @@ -292,11 +293,11 @@ public class Ver2DictDecoder extends AbstractDictDecoder { } // Insert bigrams into the fusion dictionary. 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/Ver2DictEncoder.java b/tests/src/com/android/inputmethod/latin/makedict/Ver2DictEncoder.java index a286190cb..eabde4620 100644 --- a/tests/src/com/android/inputmethod/latin/makedict/Ver2DictEncoder.java +++ b/tests/src/com/android/inputmethod/latin/makedict/Ver2DictEncoder.java @@ -18,6 +18,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.BinaryDictEncoderUtils.CodePointTable; import com.android.inputmethod.latin.makedict.FormatSpec.FormatOptions; import com.android.inputmethod.latin.makedict.FusionDictionary.PtNode; import com.android.inputmethod.latin.makedict.FusionDictionary.PtNodeArray; @@ -28,7 +29,11 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; import java.util.Iterator; +import java.util.Map.Entry; /** * An implementation of DictEncoder for version 2 binary dictionary. @@ -40,12 +45,16 @@ public class Ver2DictEncoder implements DictEncoder { private OutputStream mOutStream; private byte[] mBuffer; private int mPosition; + private final int mCodePointTableMode; + public static final int CODE_POINT_TABLE_OFF = 0; + public static final int CODE_POINT_TABLE_ON = 1; @UsedForTesting - public Ver2DictEncoder(final File dictFile) { + public Ver2DictEncoder(final File dictFile, final int codePointTableMode) { mDictFile = dictFile; mOutStream = null; mBuffer = null; + mCodePointTableMode = codePointTableMode; } // This constructor is used only by BinaryDictOffdeviceUtilsTests. @@ -55,6 +64,7 @@ public class Ver2DictEncoder implements DictEncoder { public Ver2DictEncoder(final OutputStream outStream) { mDictFile = null; mOutStream = outStream; + mCodePointTableMode = CODE_POINT_TABLE_OFF; } private void openStream() throws FileNotFoundException { @@ -68,10 +78,53 @@ public class Ver2DictEncoder implements DictEncoder { } } + // Package for testing + static CodePointTable makeCodePointTable(final FusionDictionary dict) { + final HashMap<Integer, Integer> codePointOccurrenceCounts = new HashMap<>(); + for (final WordProperty word : dict) { + // Store per code point occurrence + final String wordString = word.mWord; + for (int i = 0; i < wordString.length(); ++i) { + final int codePoint = Character.codePointAt(wordString, i); + if (codePointOccurrenceCounts.containsKey(codePoint)) { + codePointOccurrenceCounts.put(codePoint, + codePointOccurrenceCounts.get(codePoint) + 1); + } else { + codePointOccurrenceCounts.put(codePoint, 1); + } + } + } + final ArrayList<Entry<Integer, Integer>> codePointOccurrenceArray = + new ArrayList<>(codePointOccurrenceCounts.entrySet()); + // Descending order sort by occurrence (value side) + Collections.sort(codePointOccurrenceArray, new Comparator<Entry<Integer, Integer>>() { + @Override + public int compare(final Entry<Integer, Integer> a, final Entry<Integer, Integer> b) { + if (a.getValue() != b.getValue()) { + return b.getValue().compareTo(a.getValue()); + } + return b.getKey().compareTo(a.getKey()); + } + }); + int currentCodePointTableIndex = FormatSpec.MINIMAL_ONE_BYTE_CHARACTER_VALUE; + // Temporary map for writing of nodes + final HashMap<Integer, Integer> codePointToOneByteCodeMap = new HashMap<>(); + for (final Entry<Integer, Integer> entry : codePointOccurrenceArray) { + // Put a relation from the original code point to the one byte code. + codePointToOneByteCodeMap.put(entry.getKey(), currentCodePointTableIndex); + if (FormatSpec.MAXIMAL_ONE_BYTE_CHARACTER_VALUE < ++currentCodePointTableIndex) { + break; + } + } + // codePointToOneByteCodeMap for writing the trie + // codePointOccurrenceArray for writing the header + return new CodePointTable(codePointToOneByteCodeMap, codePointOccurrenceArray); + } + @Override public void writeDictionary(final FusionDictionary dict, final FormatOptions formatOptions) throws IOException, UnsupportedFormatException { - if (formatOptions.mVersion > FormatSpec.VERSION2) { + if (formatOptions.mVersion > FormatSpec.VERSION201) { throw new UnsupportedFormatException( "The given format options has wrong version number : " + formatOptions.mVersion); @@ -80,7 +133,18 @@ public class Ver2DictEncoder implements DictEncoder { if (mOutStream == null) { openStream(); } - BinaryDictEncoderUtils.writeDictionaryHeader(mOutStream, dict, formatOptions); + + // Make code point conversion table ordered by occurrence of code points + // Version 201 or later have codePointTable + final CodePointTable codePointTable; + if (formatOptions.mVersion >= FormatSpec.MINIMUM_SUPPORTED_VERSION_OF_CODE_POINT_TABLE) { + codePointTable = makeCodePointTable(dict); + } else { + codePointTable = new CodePointTable(); + } + + BinaryDictEncoderUtils.writeDictionaryHeader(mOutStream, dict, formatOptions, + codePointTable.mCodePointOccurrenceArray); // 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 @@ -94,7 +158,8 @@ public class Ver2DictEncoder implements DictEncoder { ArrayList<PtNodeArray> flatNodes = BinaryDictEncoderUtils.flattenTree(dict.mRootNodeArray); MakedictLog.i("Computing addresses..."); - BinaryDictEncoderUtils.computeAddresses(dict, flatNodes); + BinaryDictEncoderUtils.computeAddresses(dict, flatNodes, + codePointTable.mCodePointToOneByteCodeMap); MakedictLog.i("Checking PtNode array..."); if (MakedictLog.DBG) BinaryDictEncoderUtils.checkFlatPtNodeArrayList(flatNodes); @@ -106,7 +171,8 @@ public class Ver2DictEncoder implements DictEncoder { MakedictLog.i("Writing file..."); for (PtNodeArray nodeArray : flatNodes) { - BinaryDictEncoderUtils.writePlacedPtNodeArray(dict, this, nodeArray); + BinaryDictEncoderUtils.writePlacedPtNodeArray(dict, this, nodeArray, + codePointTable.mCodePointToOneByteCodeMap); } if (MakedictLog.DBG) BinaryDictEncoderUtils.showStatistics(flatNodes); mOutStream.write(mBuffer, 0, mPosition); @@ -138,15 +204,19 @@ public class Ver2DictEncoder implements DictEncoder { countSize); } - private void writePtNodeFlags(final PtNode ptNode) { - final int childrenPos = BinaryDictEncoderUtils.getChildrenPosition(ptNode); + private void writePtNodeFlags(final PtNode ptNode, + final HashMap<Integer, Integer> codePointToOneByteCodeMap) { + final int childrenPos = BinaryDictEncoderUtils.getChildrenPosition(ptNode, + codePointToOneByteCodeMap); 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); + private void writeCharacters(final int[] codePoints, final boolean hasSeveralChars, + final HashMap<Integer, Integer> codePointToOneByteCodeMap) { + mPosition = CharEncoding.writeCharArray(codePoints, mBuffer, mPosition, + codePointToOneByteCodeMap); if (hasSeveralChars) { mBuffer[mPosition++] = FormatSpec.PTNODE_CHARACTERS_TERMINATOR; } @@ -159,8 +229,10 @@ public class Ver2DictEncoder implements DictEncoder { } } - private void writeChildrenPosition(final PtNode ptNode) { - final int childrenPos = BinaryDictEncoderUtils.getChildrenPosition(ptNode); + private void writeChildrenPosition(final PtNode ptNode, + final HashMap<Integer, Integer> codePointToOneByteCodeMap) { + final int childrenPos = BinaryDictEncoderUtils.getChildrenPosition(ptNode, + codePointToOneByteCodeMap); mPosition += BinaryDictEncoderUtils.writeChildrenPosition(mBuffer, mPosition, childrenPos); } @@ -170,7 +242,8 @@ public class Ver2DictEncoder implements DictEncoder { * * @param shortcuts the shortcut attributes list. */ - private void writeShortcuts(final ArrayList<WeightedString> shortcuts) { + private void writeShortcuts(final ArrayList<WeightedString> shortcuts, + final HashMap<Integer, Integer> codePointToOneByteCodeMap) { if (null == shortcuts || shortcuts.isEmpty()) return; final int indexOfShortcutByteSize = mPosition; @@ -183,7 +256,8 @@ public class Ver2DictEncoder implements DictEncoder { target.getProbability()); mPosition = BinaryDictEncoderUtils.writeUIntToBuffer(mBuffer, mPosition, shortcutFlags, FormatSpec.PTNODE_ATTRIBUTE_FLAGS_SIZE); - final int shortcutShift = CharEncoding.writeString(mBuffer, mPosition, target.mWord); + final int shortcutShift = CharEncoding.writeString(mBuffer, mPosition, target.mWord, + codePointToOneByteCodeMap); mPosition += shortcutShift; } final int shortcutByteSize = mPosition - indexOfShortcutByteSize; @@ -223,18 +297,14 @@ public class Ver2DictEncoder implements DictEncoder { } @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()); + public void writePtNode(final PtNode ptNode, final FusionDictionary dict, + final HashMap<Integer, Integer> codePointToOneByteCodeMap) { + writePtNodeFlags(ptNode, codePointToOneByteCodeMap); + writeCharacters(ptNode.mChars, ptNode.hasSeveralChars(), codePointToOneByteCodeMap); writeFrequency(ptNode.getProbability()); - writeChildrenPosition(ptNode); - writeShortcuts(ptNode.mShortcutTargets); + writeChildrenPosition(ptNode, codePointToOneByteCodeMap); + // TODO: Use codePointToOneByteCodeMap for shortcuts. + writeShortcuts(ptNode.mShortcutTargets, null /* codePointToOneByteCodeMap */); writeBigrams(ptNode.mBigrams, dict); } } diff --git a/tests/src/com/android/inputmethod/latin/makedict/Ver2DictEncoderTests.java b/tests/src/com/android/inputmethod/latin/makedict/Ver2DictEncoderTests.java new file mode 100644 index 000000000..9104c2fcb --- /dev/null +++ b/tests/src/com/android/inputmethod/latin/makedict/Ver2DictEncoderTests.java @@ -0,0 +1,91 @@ +/* + * 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.makedict; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map.Entry; + +import com.android.inputmethod.latin.makedict.BinaryDictEncoderUtils.CodePointTable; +import com.android.inputmethod.latin.makedict.FusionDictionary.PtNodeArray; + +import android.test.AndroidTestCase; +import android.test.suitebuilder.annotation.LargeTest; +import android.util.Log; + +/** + * Unit tests for Ver2DictEncoder + */ +@LargeTest +public class Ver2DictEncoderTests extends AndroidTestCase { + private static final String TAG = Ver2DictEncoderTests.class.getSimpleName(); + private static final int UNIGRAM_FREQ = 10; + + public void testCodePointTable() { + final String[] wordSource = {"words", "used", "for", "testing", "a", "code point", "table"}; + final List<String> words = Arrays.asList(wordSource); + final String correctCodePointTable = "eotdsanirfg bclwup"; + final String correctCodePointOccurrenceArrayString = + "11641114101411531003114211021052972119111711121108110311021991981321"; + final String correctCodePointExpectedMapString = "343332363540383937464549484744414243"; + final String dictName = "codePointTableTest"; + final String dictVersion = Long.toString(System.currentTimeMillis()); + + final FormatSpec.FormatOptions formatOptions = + new FormatSpec.FormatOptions(FormatSpec.VERSION2); + final FusionDictionary sourcedict = new FusionDictionary(new PtNodeArray(), + BinaryDictUtils.makeDictionaryOptions(dictName, dictVersion, formatOptions)); + addUnigrams(sourcedict, words, null /* shortcutMap */); + final CodePointTable codePointTable = Ver2DictEncoder.makeCodePointTable(sourcedict); + + // Check if mCodePointOccurrenceArray is correct + final StringBuilder codePointOccurrenceArrayString = new StringBuilder(); + for (Entry<Integer, Integer> entry : codePointTable.mCodePointOccurrenceArray) { + codePointOccurrenceArrayString.append(entry.getKey()); + codePointOccurrenceArrayString.append(entry.getValue()); + } + assertEquals(correctCodePointOccurrenceArrayString, + codePointOccurrenceArrayString.toString()); + + // Check if mCodePointToOneByteCodeMap is correct + final StringBuilder codePointExpectedMapString = new StringBuilder(); + for (int i = 0; i < correctCodePointTable.length(); ++i) { + codePointExpectedMapString.append(codePointTable.mCodePointToOneByteCodeMap.get( + correctCodePointTable.codePointAt(i))); + } + assertEquals(correctCodePointExpectedMapString, codePointExpectedMapString.toString()); + } + + /** + * Adds unigrams to the dictionary. + */ + private void addUnigrams(final FusionDictionary dict, final List<String> words, + final HashMap<String, List<String>> shortcutMap) { + for (final String word : words) { + 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 */); + } + } +} diff --git a/tests/src/com/android/inputmethod/latin/makedict/Ver4DictDecoder.java b/tests/src/com/android/inputmethod/latin/makedict/Ver4DictDecoder.java index 5e8417ed6..0da915a75 100644 --- a/tests/src/com/android/inputmethod/latin/makedict/Ver4DictDecoder.java +++ b/tests/src/com/android/inputmethod/latin/makedict/Ver4DictDecoder.java @@ -97,12 +97,13 @@ public class Ver4DictDecoder extends AbstractDictDecoder { } } // 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 index 76eaef431..3262a1623 100644 --- a/tests/src/com/android/inputmethod/latin/makedict/Ver4DictEncoder.java +++ b/tests/src/com/android/inputmethod/latin/makedict/Ver4DictEncoder.java @@ -19,7 +19,7 @@ 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.NgramContext; import com.android.inputmethod.latin.makedict.FormatSpec.FormatOptions; import com.android.inputmethod.latin.makedict.FusionDictionary.PtNode; import com.android.inputmethod.latin.utils.BinaryDictionaryUtils; @@ -27,6 +27,7 @@ import com.android.inputmethod.latin.utils.LocaleUtils; import java.io.File; import java.io.IOException; +import java.util.HashMap; /** * An implementation of DictEncoder for version 4 binary dictionary. @@ -102,14 +103,15 @@ public class Ver4DictEncoder implements DictEncoder { } } 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, + if (!word0Property.mHasNgrams) continue; + // TODO: Support ngram. + for (final WeightedString word1 : word0Property.getBigrams()) { + final NgramContext ngramContext = + new NgramContext(new NgramContext.WordInfo(word0Property.mWord)); + if (!binaryDict.addNgramEntry(ngramContext, word1.mWord, word1.getProbability(), 0 /* timestamp */)) { MakedictLog.e("Cannot add n-gram entry for " - + prevWordsInfo + " -> " + word1.mWord); + + ngramContext + " -> " + word1.mWord); return; } if (binaryDict.needsToRunGC(true /* mindsBlockByGC */)) { @@ -141,10 +143,7 @@ public class Ver4DictEncoder implements DictEncoder { } @Override - public void writeForwardLinkAddress(int forwardLinkAddress) { - } - - @Override - public void writePtNode(PtNode ptNode, FusionDictionary dict) { + public void writePtNode(PtNode ptNode, FusionDictionary dict, + HashMap<Integer, Integer> codePointToOneByteCodeMap) { } } 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..d151732aa --- /dev/null +++ b/tests/src/com/android/inputmethod/latin/network/BlockingHttpClientTests.java @@ -0,0 +1,174 @@ +/* + * 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.Mockito.any; +import static org.mockito.Mockito.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 { + when(mMockHttpConnection.getResponseCode()).thenReturn(HttpURLConnection.HTTP_BAD_GATEWAY); + final BlockingHttpClient client = new BlockingHttpClient(mMockHttpConnection); + final FakeErrorResponseProcessor processor = + new FakeErrorResponseProcessor(HttpURLConnection.HTTP_BAD_GATEWAY); + + client.execute(null /* empty request */, processor); + assertTrue("ResponseProcessor was not invoked", processor.mInvoked); + } + + public void testError_clientTimeout() throws IOException { + when(mMockHttpConnection.getResponseCode()).thenReturn( + HttpURLConnection.HTTP_CLIENT_TIMEOUT); + final BlockingHttpClient client = new BlockingHttpClient(mMockHttpConnection); + final FakeErrorResponseProcessor processor = + new FakeErrorResponseProcessor(HttpURLConnection.HTTP_CLIENT_TIMEOUT); + + client.execute(null /* empty request */, processor); + assertTrue("ResponseProcessor was not invoked", processor.mInvoked); + } + + public void testError_forbiddenWithRequest() throws IOException { + 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(HttpURLConnection.HTTP_FORBIDDEN); + + client.execute(new byte[100], processor); + verify(mockOutputStream).write(any(byte[].class), eq(0), eq(100)); + assertTrue("ResponseProcessor was not invoked", processor.mInvoked); + } + + public void testSuccess_emptyRequest() throws IOException { + 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 IOException { + 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); + } + + private static class FakeErrorResponseProcessor implements ResponseProcessor { + private final int mExpectedStatusCode; + + boolean mInvoked; + + FakeErrorResponseProcessor(int expectedStatusCode) { + mExpectedStatusCode = expectedStatusCode; + } + + @Override + public void onError(int httpStatusCode, String message) { + mInvoked = true; + assertEquals("onError:", mExpectedStatusCode, httpStatusCode); + } + + @Override + public void onSuccess(InputStream response) { + fail("Expected an error but received success"); + } + } + + private static class FakeSuccessResponseProcessor implements ResponseProcessor { + private final byte[] mExpectedResponse; + + boolean mInvoked; + + FakeSuccessResponseProcessor(byte[] expectedResponse) { + mExpectedResponse = expectedResponse; + } + + @Override + public void onError(int httpStatusCode, String message) { + fail("Expected a response but received an error"); + } + + @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"); + } + } + } +} 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..2b43d5b14 --- /dev/null +++ b/tests/src/com/android/inputmethod/latin/network/HttpUrlConnectionBuilderTests.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.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()); + } +} diff --git a/tests/src/com/android/inputmethod/latin/personalization/PersonalizationDictionaryTests.java b/tests/src/com/android/inputmethod/latin/personalization/PersonalizationDictionaryTests.java index 0f2f9814b..4e7e8140a 100644 --- a/tests/src/com/android/inputmethod/latin/personalization/PersonalizationDictionaryTests.java +++ b/tests/src/com/android/inputmethod/latin/personalization/PersonalizationDictionaryTests.java @@ -29,6 +29,7 @@ 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.RichInputMethodManager; import com.android.inputmethod.latin.ExpandableBinaryDictionary.AddMultipleDictionaryEntriesCallback; import com.android.inputmethod.latin.makedict.CodePointUtils; import com.android.inputmethod.latin.settings.SpacingAndPunctuations; @@ -36,6 +37,7 @@ import com.android.inputmethod.latin.settings.SpacingAndPunctuations; import android.test.AndroidTestCase; import android.test.suitebuilder.annotation.LargeTest; import android.util.Log; +import android.view.inputmethod.InputMethodSubtype; /** * Unit tests for personalization dictionary @@ -52,19 +54,31 @@ public class PersonalizationDictionaryTests extends AndroidTestCase { final ArrayList<String> dictTypes = new ArrayList<>(); dictTypes.add(Dictionary.TYPE_MAIN); dictTypes.add(Dictionary.TYPE_PERSONALIZATION); - final DictionaryFacilitator dictionaryFacilitator = new DictionaryFacilitator(); + final DictionaryFacilitator dictionaryFacilitator = new DictionaryFacilitator(getContext()); dictionaryFacilitator.resetDictionariesForTesting(getContext(), LOCALE_EN_US, dictTypes, new HashMap<String, File>(), new HashMap<String, Map<String, String>>()); + // Set subtypes. + RichInputMethodManager.init(getContext()); + final RichInputMethodManager richImm = RichInputMethodManager.getInstance(); + final ArrayList<InputMethodSubtype> subtypes = new ArrayList<>(); + subtypes.add(richImm.findSubtypeByLocaleAndKeyboardLayoutSet( + LOCALE_EN_US.toString(), "qwerty")); + dictionaryFacilitator.updateEnabledSubtypes(subtypes); return dictionaryFacilitator; } public void testAddManyTokens() { final DictionaryFacilitator dictionaryFacilitator = getDictionaryFacilitator(); dictionaryFacilitator.clearPersonalizationDictionary(); - final int dataChunkCount = 20; - final int wordCountInOneChunk = 2000; + final int dataChunkCount = 100; + final int wordCountInOneChunk = 200; + final int uniqueWordCount = 100; final Random random = new Random(System.currentTimeMillis()); final int[] codePointSet = CodePointUtils.LATIN_ALPHABETS_LOWER; + final ArrayList<String> words = new ArrayList<>(); + for (int i = 0; i < uniqueWordCount; i++) { + words.add(CodePointUtils.generateWord(random, codePointSet)); + } final SpacingAndPunctuations spacingAndPunctuations = new SpacingAndPunctuations(getContext().getResources()); @@ -75,10 +89,11 @@ public class PersonalizationDictionaryTests extends AndroidTestCase { 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)); + tokens.add(words.get(random.nextInt(words.size()))); } final PersonalizationDataChunk personalizationDataChunk = new PersonalizationDataChunk( - true /* inputByUser */, tokens, timeStampInSeconds, DUMMY_PACKAGE_NAME); + true /* inputByUser */, tokens, timeStampInSeconds, DUMMY_PACKAGE_NAME, + LOCALE_EN_US.getLanguage()); final CountDownLatch countDownLatch = new CountDownLatch(1); final AddMultipleDictionaryEntriesCallback callback = new AddMultipleDictionaryEntriesCallback() { diff --git a/tests/src/com/android/inputmethod/latin/personalization/UserHistoryDictionaryTests.java b/tests/src/com/android/inputmethod/latin/personalization/UserHistoryDictionaryTests.java index f87f3b494..766627334 100644 --- a/tests/src/com/android/inputmethod/latin/personalization/UserHistoryDictionaryTests.java +++ b/tests/src/com/android/inputmethod/latin/personalization/UserHistoryDictionaryTests.java @@ -21,13 +21,14 @@ 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.NgramContext; +import com.android.inputmethod.latin.NgramContext.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.io.FilenameFilter; import java.util.ArrayList; import java.util.HashSet; import java.util.List; @@ -41,6 +42,8 @@ import java.util.concurrent.TimeUnit; @LargeTest public class UserHistoryDictionaryTests extends AndroidTestCase { private static final String TAG = UserHistoryDictionaryTests.class.getSimpleName(); + private static final int WAIT_FOR_WRITING_FILE_IN_MILLISECONDS = 3000; + private static final String TEST_LOCALE_PREFIX = "test_"; private static final String[] CHARACTERS = { "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", @@ -49,14 +52,60 @@ public class UserHistoryDictionaryTests extends AndroidTestCase { private int mCurrentTime = 0; + private void removeAllTestDictFiles() { + final Locale dummyLocale = new Locale(TEST_LOCALE_PREFIX); + final String dictName = ExpandableBinaryDictionary.getDictName( + UserHistoryDictionary.NAME, dummyLocale, null /* dictFile */); + final File dictFile = ExpandableBinaryDictionary.getDictFile( + mContext, dictName, null /* dictFile */); + final FilenameFilter filenameFilter = new FilenameFilter() { + @Override + public boolean accept(File dir, String filename) { + return filename.startsWith(UserHistoryDictionary.NAME + "." + TEST_LOCALE_PREFIX); + } + }; + FileUtils.deleteFilteredFiles(dictFile.getParentFile(), filenameFilter); + } + + private void printAllFiles(final File dir) { + Log.d(TAG, dir.getAbsolutePath()); + for (final File file : dir.listFiles()) { + Log.d(TAG, " " + file.getName()); + } + } + + private void checkExistenceAndRemoveDictFile(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("check exisiting of " + dictFile, dictFile.exists()); + FileUtils.deleteRecursively(dictFile); + } + + private static Locale getDummyLocale(final String name) { + return new Locale(TEST_LOCALE_PREFIX + name + System.currentTimeMillis()); + } + @Override protected void setUp() throws Exception { super.setUp(); resetCurrentTimeForTestMode(); + removeAllTestDictFiles(); } @Override protected void tearDown() throws Exception { + removeAllTestDictFiles(); stopTestModeInNativeCode(); super.tearDown(); } @@ -110,13 +159,13 @@ public class UserHistoryDictionaryTests extends AndroidTestCase { return new ArrayList<>(wordSet); } - private static void addToDict(final UserHistoryDictionary dict, final List<String> words) { - PrevWordsInfo prevWordsInfo = PrevWordsInfo.EMPTY_PREV_WORDS_INFO; + private static void addToDict(final UserHistoryDictionary dict, final List<String> words, + final int timestamp) { + NgramContext ngramContext = NgramContext.EMPTY_PREV_WORDS_INFO; for (String word : words) { - UserHistoryDictionary.addToDictionary(dict, prevWordsInfo, word, true, - (int)TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis()), + UserHistoryDictionary.addToDictionary(dict, ngramContext, word, true, timestamp, DistracterFilter.EMPTY_DISTRACTER_FILTER); - prevWordsInfo = prevWordsInfo.getNextPrevWordsInfo(new WordInfo(word)); + ngramContext = ngramContext.getNextNgramContext(new WordInfo(word)); } } @@ -124,13 +173,11 @@ public class UserHistoryDictionaryTests extends AndroidTestCase { * @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) { + private void addAndWriteRandomWords(final UserHistoryDictionary dict, + 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); + addToDict(dict, words, mCurrentTime); if (checkContents) { dict.waitAllTasksForTests(); for (int i = 0; i < numberOfWords; ++i) { @@ -144,49 +191,31 @@ public class UserHistoryDictionaryTests extends AndroidTestCase { /** * 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 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() { 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 Locale dummyLocale = getDummyLocale("random_words"); final String dictName = ExpandableBinaryDictionary.getDictName( UserHistoryDictionary.NAME, dummyLocale, null /* dictFile */); final File dictFile = ExpandableBinaryDictionary.getDictFile( mContext, dictName, null /* dictFile */); + final UserHistoryDictionary dict = PersonalizationHelper.getUserHistoryDictionary( + getContext(), dummyLocale); final int numberOfWords = 1000; final Random random = new Random(123456); - - 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); - } + clearHistory(dict); + addAndWriteRandomWords(dict, numberOfWords, random, true /* checksContents */); + checkExistenceAndRemoveDictFile(dict, dictFile); } public void testStressTestForSwitchingLanguagesAndAddingWords() { @@ -195,79 +224,75 @@ public class UserHistoryDictionaryTests extends AndroidTestCase { 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 Locale dummyLocale = getDummyLocale("switching_languages" + i); final String dictName = ExpandableBinaryDictionary.getDictName( - UserHistoryDictionary.NAME, dummyLocales[i], null /* dictFile */); + UserHistoryDictionary.NAME, dummyLocale, null /* dictFile */); dictFiles[i] = ExpandableBinaryDictionary.getDictFile( mContext, dictName, null /* dictFile */); - clearHistory(dummyLocales[i]); + dicts[i] = PersonalizationHelper.getUserHistoryDictionary(getContext(), + dummyLocale); + 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]. + addAndWriteRandomWords(dicts[index], numberOfWordsInsertedForEachLanguageSwitch, + random, false /* checksContents */); } 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); + checkExistenceAndRemoveDictFile(dicts[i], dictFiles[i]); } } } public void testAddManyWords() { - final Locale dummyLocale = new Locale("test_random_words" + System.currentTimeMillis()); + final Locale dummyLocale = getDummyLocale("many_random_words"); final String dictName = ExpandableBinaryDictionary.getDictName( UserHistoryDictionary.NAME, dummyLocale, null /* dictFile */); final File dictFile = ExpandableBinaryDictionary.getDictFile( mContext, dictName, null /* dictFile */); final int numberOfWords = 10000; final Random random = new Random(123456); - clearHistory(dummyLocale); + final UserHistoryDictionary dict = PersonalizationHelper.getUserHistoryDictionary( + getContext(), dummyLocale); + clearHistory(dict); try { - addAndWriteRandomWords(dummyLocale, numberOfWords, random, true /* checksContents */); + addAndWriteRandomWords(dict, numberOfWords, random, true /* checksContents */); } finally { - Log.d(TAG, "waiting for writing ..."); - waitForWriting(dummyLocale); - assertTrue("check exisiting of " + dictFile, dictFile.exists()); - FileUtils.deleteRecursively(dictFile); + checkExistenceAndRemoveDictFile(dict, dictFile); } } public void testDecaying() { - final Locale dummyLocale = new Locale("test_decaying" + System.currentTimeMillis()); + final Locale dummyLocale = getDummyLocale("decaying"); + final UserHistoryDictionary dict = PersonalizationHelper.getUserHistoryDictionary( + getContext(), dummyLocale); final int numberOfWords = 5000; final Random random = new Random(123456); resetCurrentTimeForTestMode(); - clearHistory(dummyLocale); + clearHistory(dict); final List<String> words = generateWords(numberOfWords, random); - final UserHistoryDictionary dict = - PersonalizationHelper.getUserHistoryDictionary(getContext(), dummyLocale); dict.waitAllTasksForTests(); - PrevWordsInfo prevWordsInfo = PrevWordsInfo.EMPTY_PREV_WORDS_INFO; + NgramContext ngramContext = NgramContext.EMPTY_PREV_WORDS_INFO; for (final String word : words) { - UserHistoryDictionary.addToDictionary(dict, prevWordsInfo, word, true, mCurrentTime, + UserHistoryDictionary.addToDictionary(dict, ngramContext, word, true, mCurrentTime, DistracterFilter.EMPTY_DISTRACTER_FILTER); - prevWordsInfo = prevWordsInfo.getNextPrevWordsInfo(new WordInfo(word)); + ngramContext = ngramContext.getNextNgramContext(new WordInfo(word)); dict.waitAllTasksForTests(); assertTrue(dict.isInDictionary(word)); } 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..2ef8b548f --- /dev/null +++ b/tests/src/com/android/inputmethod/latin/settings/AccountsSettingsFragmentTests.java @@ -0,0 +1,132 @@ +/* + * 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 android.app.AlertDialog; +import android.app.Dialog; +import android.content.Intent; +import android.test.ActivityInstrumentationTestCase2; +import android.test.suitebuilder.annotation.MediumTest; +import android.view.View; +import android.widget.ListView; + +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; + + private AlertDialog mDialog; + + public AccountsSettingsFragmentTests() { + super(TestFragmentActivity.class); + } + + @Override + protected void setUp() throws Exception { + super.setUp(); + Intent intent = new Intent(); + intent.putExtra(TestFragmentActivity.EXTRA_SHOW_FRAGMENT, FRAG_NAME); + setActivityIntent(intent); + } + + public void testEmptyAccounts() { + final AccountsSettingsFragment fragment = + (AccountsSettingsFragment) getActivity().mFragment; + try { + fragment.createAccountPicker(new String[0], null); + fail("Expected IllegalArgumentException, never thrown"); + } catch (IllegalArgumentException expected) { + // Expected. + } + } + + public void testMultipleAccounts_noCurrentAccount() { + final AccountsSettingsFragment fragment = + (AccountsSettingsFragment) getActivity().mFragment; + final CountDownLatch latch = new CountDownLatch(1); + getActivity().runOnUiThread(new Runnable() { + @Override + public void run() { + mDialog = fragment.createAccountPicker( + new String[] { + "1@example.com", + "2@example.com", + "3@example.com", + "4@example.com"}, + null); + mDialog.show(); + latch.countDown(); + } + }); + + try { + latch.await(TEST_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS); + } catch (InterruptedException ex) { + fail(); + } + getInstrumentation().waitForIdleSync(); + final ListView lv = mDialog.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, mDialog.getButton(Dialog.BUTTON_NEUTRAL).getVisibility()); + assertEquals(View.VISIBLE, mDialog.getButton(Dialog.BUTTON_NEGATIVE).getVisibility()); + assertEquals(View.VISIBLE, mDialog.getButton(Dialog.BUTTON_POSITIVE).getVisibility()); + } + + public void testMultipleAccounts_currentAccount() { + final AccountsSettingsFragment fragment = + (AccountsSettingsFragment) getActivity().mFragment; + final CountDownLatch latch = new CountDownLatch(1); + getActivity().runOnUiThread(new Runnable() { + @Override + public void run() { + mDialog = fragment.createAccountPicker( + new String[] { + "1@example.com", + "2@example.com", + "3@example.com", + "4@example.com"}, + "3@example.com"); + mDialog.show(); + latch.countDown(); + } + }); + + try { + latch.await(TEST_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS); + } catch (InterruptedException ex) { + fail(); + } + getInstrumentation().waitForIdleSync(); + final ListView lv = mDialog.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, mDialog.getButton(Dialog.BUTTON_NEUTRAL).getVisibility()); + assertEquals(View.VISIBLE, mDialog.getButton(Dialog.BUTTON_NEGATIVE).getVisibility()); + assertEquals(View.VISIBLE, mDialog.getButton(Dialog.BUTTON_POSITIVE).getVisibility()); + } +} diff --git a/tests/src/com/android/inputmethod/latin/utils/AdditionalSubtypeUtilsTests.java b/tests/src/com/android/inputmethod/latin/utils/AdditionalSubtypeUtilsTests.java index 91c9c3775..66a12b99b 100644 --- a/tests/src/com/android/inputmethod/latin/utils/AdditionalSubtypeUtilsTests.java +++ b/tests/src/com/android/inputmethod/latin/utils/AdditionalSubtypeUtilsTests.java @@ -151,25 +151,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/CapsModeUtilsTests.java b/tests/src/com/android/inputmethod/latin/utils/CapsModeUtilsTests.java index c746c8345..4646a823d 100644 --- a/tests/src/com/android/inputmethod/latin/utils/CapsModeUtilsTests.java +++ b/tests/src/com/android/inputmethod/latin/utils/CapsModeUtilsTests.java @@ -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/DistracterFilterTest.java b/tests/src/com/android/inputmethod/latin/utils/DistracterFilterTest.java index e6fb28260..8360d53fb 100644 --- a/tests/src/com/android/inputmethod/latin/DistracterFilterTest.java +++ b/tests/src/com/android/inputmethod/latin/utils/DistracterFilterTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.inputmethod.latin; +package com.android.inputmethod.latin.utils; import java.util.ArrayList; import java.util.Locale; @@ -24,7 +24,9 @@ import android.test.AndroidTestCase; import android.test.suitebuilder.annotation.LargeTest; import android.view.inputmethod.InputMethodSubtype; -import com.android.inputmethod.latin.utils.DistracterFilterCheckingExactMatchesAndSuggestions; +import com.android.inputmethod.latin.NgramContext; +import com.android.inputmethod.latin.RichInputMethodManager; +import com.android.inputmethod.latin.utils.DistracterFilter.HandlingType; /** * Unit test for DistracterFilter @@ -50,8 +52,13 @@ public class DistracterFilterTest extends AndroidTestCase { mDistracterFilter.updateEnabledSubtypes(subtypes); } - public void testIsDistractorToWordsInDictionaries() { - final PrevWordsInfo EMPTY_PREV_WORDS_INFO = PrevWordsInfo.EMPTY_PREV_WORDS_INFO; + @Override + protected void tearDown() { + mDistracterFilter.close(); + } + + public void testIsDistracterToWordsInDictionaries() { + final NgramContext EMPTY_PREV_WORDS_INFO = NgramContext.EMPTY_PREV_WORDS_INFO; final Locale localeEnUs = new Locale("en", "US"); String typedWord; @@ -194,4 +201,25 @@ public class DistracterFilterTest extends AndroidTestCase { assertTrue(mDistracterFilter.isDistracterToWordsInDictionaries( EMPTY_PREV_WORDS_INFO, typedWord, localeFrFr)); } + + public void testGetWordHandlingType() { + final Locale localeEnUs = new Locale("en", "US"); + final NgramContext EMPTY_PREV_WORDS_INFO = NgramContext.EMPTY_PREV_WORDS_INFO; + int handlingType = 0; + + handlingType = mDistracterFilter.getWordHandlingType(EMPTY_PREV_WORDS_INFO, + "this", localeEnUs); + assertFalse(HandlingType.shouldBeLowerCased(handlingType)); + assertFalse(HandlingType.shouldBeHandledAsOov(handlingType)); + + handlingType = mDistracterFilter.getWordHandlingType(EMPTY_PREV_WORDS_INFO, + "This", localeEnUs); + assertTrue(HandlingType.shouldBeLowerCased(handlingType)); + assertFalse(HandlingType.shouldBeHandledAsOov(handlingType)); + + handlingType = mDistracterFilter.getWordHandlingType(EMPTY_PREV_WORDS_INFO, + "thibk", localeEnUs); + assertFalse(HandlingType.shouldBeLowerCased(handlingType)); + assertTrue(HandlingType.shouldBeHandledAsOov(handlingType)); + } } diff --git a/tests/src/com/android/inputmethod/latin/utils/ImportantNoticeUtilsTests.java b/tests/src/com/android/inputmethod/latin/utils/ImportantNoticeUtilsTests.java index 819d76328..cbabf7e8d 100644 --- a/tests/src/com/android/inputmethod/latin/utils/ImportantNoticeUtilsTests.java +++ b/tests/src/com/android/inputmethod/latin/utils/ImportantNoticeUtilsTests.java @@ -16,18 +16,18 @@ 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 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 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; @@ -112,6 +112,28 @@ 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 imortant notice after fresh install", true, + ImportantNoticeUtils.hasNewImportantNotice(getContext())); + assertEquals("Next important norice 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())); + } + public void testUpdateVersion() { mImportantNoticePreferences.clear(); @@ -163,7 +185,7 @@ public class ImportantNoticeUtilsTests extends AndroidTestCase { 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()))); @@ -180,7 +202,7 @@ public class ImportantNoticeUtilsTests extends AndroidTestCase { 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()))); @@ -196,7 +218,7 @@ public class ImportantNoticeUtilsTests extends AndroidTestCase { 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()))); @@ -212,7 +234,7 @@ public class ImportantNoticeUtilsTests extends AndroidTestCase { 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()))); diff --git a/tests/src/com/android/inputmethod/latin/utils/SpacebarLanguageUtilsTests.java b/tests/src/com/android/inputmethod/latin/utils/SpacebarLanguageUtilsTests.java new file mode 100644 index 000000000..b766ab2e7 --- /dev/null +++ b/tests/src/com/android/inputmethod/latin/utils/SpacebarLanguageUtilsTests.java @@ -0,0 +1,239 @@ +/* + * 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 com.android.inputmethod.latin.RichInputMethodSubtype; + +import java.util.ArrayList; +import java.util.Locale; + +@SmallTest +public class SpacebarLanguageUtilsTests extends AndroidTestCase { + // All input method subtypes of LatinIME. + private final ArrayList<RichInputMethodSubtype> mSubtypesList = new ArrayList<>(); + + private RichInputMethodManager mRichImm; + private Resources mRes; + + RichInputMethodSubtype EN_US; + RichInputMethodSubtype EN_GB; + RichInputMethodSubtype ES_US; + RichInputMethodSubtype FR; + RichInputMethodSubtype FR_CA; + RichInputMethodSubtype FR_CH; + RichInputMethodSubtype DE; + RichInputMethodSubtype DE_CH; + RichInputMethodSubtype HI_ZZ; + 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; + + @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(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_ZZ = new RichInputMethodSubtype(mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet( + "hi_ZZ", "qwerty")); + 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")); + } + + 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()); + 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()); + if (SubtypeLocaleUtils.sExceptionalLocaleDisplayedInRootLocale.contains( + subtype.getLocale())) { + // Skip test because the language part of this locale string doesn't represent + // the locale to be displayed on the spacebar (for example hi_ZZ and Hinglish). + continue; + } + final String spacebarText = subtype.getMiddleDisplayName(); + if (subtype.isNoLanguage()) { + assertEquals(subtypeName, SubtypeLocaleUtils.getKeyboardLayoutSetDisplayName( + subtype.getRawSubtype()), 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) + // hi_ZZ qwerty F Hinglish Hinglish + // 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)", 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_ZZ", "Hinglish", HI_ZZ.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("hi_ZZ", "Hinglish", HI_ZZ.getMiddleDisplayName()); + assertEquals("zz", "QWERTY", ZZ.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()); + 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/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/SubtypeLocaleUtilsTests.java b/tests/src/com/android/inputmethod/latin/utils/SubtypeLocaleUtilsTests.java index ce3df7dd6..c095e6831 100644 --- a/tests/src/com/android/inputmethod/latin/utils/SubtypeLocaleUtilsTests.java +++ b/tests/src/com/android/inputmethod/latin/utils/SubtypeLocaleUtilsTests.java @@ -24,6 +24,7 @@ import android.view.inputmethod.InputMethodInfo; import android.view.inputmethod.InputMethodSubtype; import com.android.inputmethod.latin.RichInputMethodManager; +import com.android.inputmethod.latin.RichInputMethodSubtype; import java.util.ArrayList; import java.util.Locale; @@ -31,7 +32,7 @@ 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; @@ -44,6 +45,7 @@ public class SubtypeLocaleUtilsTests extends AndroidTestCase { InputMethodSubtype FR_CH; InputMethodSubtype DE; InputMethodSubtype DE_CH; + InputMethodSubtype HI_ZZ; InputMethodSubtype ZZ; InputMethodSubtype DE_QWERTY; InputMethodSubtype FR_QWERTZ; @@ -66,7 +68,7 @@ public class SubtypeLocaleUtilsTests extends AndroidTestCase { 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 +87,8 @@ public class SubtypeLocaleUtilsTests extends AndroidTestCase { Locale.GERMAN.toString(), "qwertz"); DE_CH = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet( "de_CH", "swiss"); + HI_ZZ = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet( + "hi_ZZ", "qwerty"); ZZ = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet( SubtypeLocaleUtils.NO_LANGUAGE, "qwerty"); DE_QWERTY = AdditionalSubtypeUtils.createAsciiEmojiCapableAdditionalSubtype( @@ -104,12 +108,12 @@ public class SubtypeLocaleUtilsTests extends AndroidTestCase { } 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 @@ -128,6 +132,7 @@ 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_ZZ", "qwerty", SubtypeLocaleUtils.getKeyboardLayoutSetName(HI_ZZ)); assertEquals("zz", "qwerty", SubtypeLocaleUtils.getKeyboardLayoutSetName(ZZ)); assertEquals("de qwerty", "qwerty", SubtypeLocaleUtils.getKeyboardLayoutSetName(DE_QWERTY)); @@ -154,6 +159,7 @@ public class SubtypeLocaleUtilsTests extends AndroidTestCase { // fr_CH swiss F French (Switzerland) // de qwertz F German // de_CH swiss F German (Switzerland) + // hi_ZZ qwerty F Hinglish // zz qwerty F Alphabet (QWERTY) // fr qwertz T French (QWERTZ) // de qwerty T German (QWERTY) @@ -182,6 +188,8 @@ public class SubtypeLocaleUtilsTests extends AndroidTestCase { SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(DE)); assertEquals("de_CH", "German (Switzerland)", SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(DE_CH)); + assertEquals("hi_ZZ", "Hinglish", + SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(HI_ZZ)); assertEquals("zz", "Alphabet (QWERTY)", SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(ZZ)); return null; @@ -226,6 +234,7 @@ public class SubtypeLocaleUtilsTests extends AndroidTestCase { // fr_CH swiss F Français (Suisse) // de qwertz F Allemand // de_CH swiss F Allemand (Suisse) + // hi_ZZ qwerty F Hinglish // zz qwerty F Alphabet latin (QWERTY) // fr qwertz T Français (QWERTZ) // de qwerty T Allemand (QWERTY) @@ -254,6 +263,8 @@ public class SubtypeLocaleUtilsTests extends AndroidTestCase { SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(DE)); assertEquals("de_CH", "Allemand (Suisse)", SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(DE_CH)); + assertEquals("hi_ZZ", "Hindi/Anglais", + SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(HI_ZZ)); assertEquals("zz", "Alphabet latin (QWERTY)", SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(ZZ)); return null; @@ -298,10 +309,12 @@ 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)) { + .getSubtypeDisplayNameInSystemLocale(rawSubtype); + if (rawSubtype.equals(ARABIC) || rawSubtype.equals(FARSI) + || rawSubtype.equals(HEBREW)) { assertTrue(subtypeName, SubtypeLocaleUtils.isRtlLanguage(subtype)); } else { assertFalse(subtypeName, SubtypeLocaleUtils.isRtlLanguage(subtype)); |