UserHistoryDictionaryTests.java revision d302b98ce63743bde9d8d8c14755b5cf71c4e7a3
1/* 2 * Copyright (C) 2012 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.inputmethod.latin.personalization; 18 19import android.test.AndroidTestCase; 20import android.test.suitebuilder.annotation.LargeTest; 21import android.util.Log; 22 23import com.android.inputmethod.latin.BinaryDictionary; 24import com.android.inputmethod.latin.ExpandableBinaryDictionary; 25import com.android.inputmethod.latin.utils.CollectionUtils; 26import com.android.inputmethod.latin.utils.FileUtils; 27 28import java.io.File; 29import java.util.ArrayList; 30import java.util.List; 31import java.util.Locale; 32import java.util.Random; 33import java.util.Set; 34import java.util.concurrent.TimeUnit; 35 36/** 37 * Unit tests for UserHistoryDictionary 38 */ 39@LargeTest 40public class UserHistoryDictionaryTests extends AndroidTestCase { 41 private static final String TAG = UserHistoryDictionaryTests.class.getSimpleName(); 42 43 private static final String[] CHARACTERS = { 44 "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", 45 "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z" 46 }; 47 48 private int mCurrentTime = 0; 49 50 @Override 51 protected void setUp() throws Exception { 52 super.setUp(); 53 resetCurrentTimeForTestMode(); 54 } 55 56 @Override 57 protected void tearDown() throws Exception { 58 stopTestModeInNativeCode(); 59 super.tearDown(); 60 } 61 62 private void resetCurrentTimeForTestMode() { 63 mCurrentTime = 0; 64 setCurrentTimeForTestMode(mCurrentTime); 65 } 66 67 private void forcePassingShortTime() { 68 // 3 days. 69 final int timeToElapse = (int)TimeUnit.DAYS.toSeconds(3); 70 mCurrentTime += timeToElapse; 71 setCurrentTimeForTestMode(mCurrentTime); 72 } 73 74 private void forcePassingLongTime() { 75 // 60 days. 76 final int timeToElapse = (int)TimeUnit.DAYS.toSeconds(60); 77 mCurrentTime += timeToElapse; 78 setCurrentTimeForTestMode(mCurrentTime); 79 } 80 81 private static int setCurrentTimeForTestMode(final int currentTime) { 82 return BinaryDictionary.setCurrentTimeForTest(currentTime); 83 } 84 85 private static int stopTestModeInNativeCode() { 86 return BinaryDictionary.setCurrentTimeForTest(-1); 87 } 88 89 /** 90 * Generates a random word. 91 */ 92 private static String generateWord(final int value) { 93 final int lengthOfChars = CHARACTERS.length; 94 StringBuilder builder = new StringBuilder(); 95 long lvalue = Math.abs((long)value); 96 while (lvalue > 0) { 97 builder.append(CHARACTERS[(int)(lvalue % lengthOfChars)]); 98 lvalue /= lengthOfChars; 99 } 100 return builder.toString(); 101 } 102 103 private static List<String> generateWords(final int number, final Random random) { 104 final Set<String> wordSet = CollectionUtils.newHashSet(); 105 while (wordSet.size() < number) { 106 wordSet.add(generateWord(random.nextInt())); 107 } 108 return new ArrayList<String>(wordSet); 109 } 110 111 private static void addToDict(final UserHistoryDictionary dict, final List<String> words) { 112 String prevWord = null; 113 for (String word : words) { 114 dict.addToDictionary(prevWord, word, true, 115 (int)TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis())); 116 prevWord = word; 117 } 118 } 119 120 /** 121 * @param checkContents if true, checks whether written words are actually in the dictionary 122 * or not. 123 */ 124 private void addAndWriteRandomWords(final Locale locale, final int numberOfWords, 125 final Random random, final boolean checkContents) { 126 final List<String> words = generateWords(numberOfWords, random); 127 final UserHistoryDictionary dict = PersonalizationHelper.getUserHistoryDictionary( 128 mContext, locale); 129 // Add random words to the user history dictionary. 130 addToDict(dict, words); 131 if (checkContents) { 132 dict.waitAllTasksForTests(); 133 for (int i = 0; i < numberOfWords; ++i) { 134 final String word = words.get(i); 135 assertTrue(dict.isInUnderlyingBinaryDictionaryForTests(word)); 136 } 137 } 138 // write to file. 139 dict.close(); 140 } 141 142 /** 143 * Clear all entries in the user history dictionary. 144 * @param locale dummy locale for testing. 145 */ 146 private void clearHistory(final Locale locale) { 147 final UserHistoryDictionary dict = PersonalizationHelper.getUserHistoryDictionary( 148 mContext, locale); 149 dict.waitAllTasksForTests(); 150 dict.clearAndFlushDictionary(); 151 dict.close(); 152 dict.waitAllTasksForTests(); 153 } 154 155 /** 156 * Shut down executer and wait until all operations of user history are done. 157 * @param locale dummy locale for testing. 158 */ 159 private void waitForWriting(final Locale locale) { 160 final UserHistoryDictionary dict = PersonalizationHelper.getUserHistoryDictionary( 161 mContext, locale); 162 dict.waitAllTasksForTests(); 163 } 164 165 public void testRandomWords() { 166 Log.d(TAG, "This test can be used for profiling."); 167 Log.d(TAG, "Usage: please set UserHistoryDictionary.PROFILE_SAVE_RESTORE to true."); 168 final Locale dummyLocale = new Locale("test_random_words" + System.currentTimeMillis()); 169 final String dictName = ExpandableBinaryDictionary.getDictName( 170 UserHistoryDictionary.NAME, dummyLocale, null /* dictFile */); 171 final File dictFile = ExpandableBinaryDictionary.getDictFile( 172 mContext, dictName, null /* dictFile */); 173 174 final int numberOfWords = 1000; 175 final Random random = new Random(123456); 176 177 try { 178 clearHistory(dummyLocale); 179 addAndWriteRandomWords(dummyLocale, numberOfWords, random, 180 true /* checksContents */); 181 } finally { 182 Log.d(TAG, "waiting for writing ..."); 183 waitForWriting(dummyLocale); 184 assertTrue("check exisiting of " + dictFile, dictFile.exists()); 185 FileUtils.deleteRecursively(dictFile); 186 } 187 } 188 189 public void testStressTestForSwitchingLanguagesAndAddingWords() { 190 final int numberOfLanguages = 2; 191 final int numberOfLanguageSwitching = 80; 192 final int numberOfWordsInsertedForEachLanguageSwitch = 100; 193 194 final File dictFiles[] = new File[numberOfLanguages]; 195 final Locale dummyLocales[] = new Locale[numberOfLanguages]; 196 try { 197 final Random random = new Random(123456); 198 199 // Create filename suffixes for this test. 200 for (int i = 0; i < numberOfLanguages; i++) { 201 dummyLocales[i] = new Locale("test_switching_languages" + i); 202 final String dictName = ExpandableBinaryDictionary.getDictName( 203 UserHistoryDictionary.NAME, dummyLocales[i], null /* dictFile */); 204 dictFiles[i] = ExpandableBinaryDictionary.getDictFile( 205 mContext, dictName, null /* dictFile */); 206 clearHistory(dummyLocales[i]); 207 } 208 209 final long start = System.currentTimeMillis(); 210 211 for (int i = 0; i < numberOfLanguageSwitching; i++) { 212 final int index = i % numberOfLanguages; 213 // Switch languages to testFilenameSuffixes[index]. 214 addAndWriteRandomWords(dummyLocales[index], 215 numberOfWordsInsertedForEachLanguageSwitch, random, 216 false /* checksContents */); 217 } 218 219 final long end = System.currentTimeMillis(); 220 Log.d(TAG, "testStressTestForSwitchingLanguageAndAddingWords took " 221 + (end - start) + " ms"); 222 } finally { 223 Log.d(TAG, "waiting for writing ..."); 224 for (int i = 0; i < numberOfLanguages; i++) { 225 waitForWriting(dummyLocales[i]); 226 } 227 for (final File dictFile : dictFiles) { 228 assertTrue("check exisiting of " + dictFile, dictFile.exists()); 229 FileUtils.deleteRecursively(dictFile); 230 } 231 } 232 } 233 234 public void testAddManyWords() { 235 final Locale dummyLocale = new Locale("test_random_words" + System.currentTimeMillis()); 236 final String dictName = ExpandableBinaryDictionary.getDictName( 237 UserHistoryDictionary.NAME, dummyLocale, null /* dictFile */); 238 final File dictFile = ExpandableBinaryDictionary.getDictFile( 239 mContext, dictName, null /* dictFile */); 240 final int numberOfWords = 10000; 241 final Random random = new Random(123456); 242 clearHistory(dummyLocale); 243 try { 244 addAndWriteRandomWords(dummyLocale, numberOfWords, random, true /* checksContents */); 245 } finally { 246 Log.d(TAG, "waiting for writing ..."); 247 waitForWriting(dummyLocale); 248 assertTrue("check exisiting of " + dictFile, dictFile.exists()); 249 FileUtils.deleteRecursively(dictFile); 250 } 251 } 252 253 public void testDecaying() { 254 final Locale dummyLocale = new Locale("test_decaying" + System.currentTimeMillis()); 255 final int numberOfWords = 5000; 256 final Random random = new Random(123456); 257 resetCurrentTimeForTestMode(); 258 clearHistory(dummyLocale); 259 final List<String> words = generateWords(numberOfWords, random); 260 final UserHistoryDictionary dict = 261 PersonalizationHelper.getUserHistoryDictionary(getContext(), dummyLocale); 262 dict.waitAllTasksForTests(); 263 String prevWord = null; 264 for (final String word : words) { 265 dict.addToDictionary(prevWord, word, true, mCurrentTime); 266 prevWord = word; 267 assertTrue(dict.isInUnderlyingBinaryDictionaryForTests(word)); 268 } 269 forcePassingShortTime(); 270 dict.runGCIfRequired(); 271 dict.waitAllTasksForTests(); 272 for (final String word : words) { 273 assertTrue(dict.isInUnderlyingBinaryDictionaryForTests(word)); 274 } 275 forcePassingLongTime(); 276 dict.runGCIfRequired(); 277 dict.waitAllTasksForTests(); 278 for (final String word : words) { 279 assertFalse(dict.isInUnderlyingBinaryDictionaryForTests(word)); 280 } 281 } 282} 283