UserHistoryDictionaryTests.java revision a4fb0cc266dc5f31db9f8ad9400c934cb958c27c
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.ExpandableBinaryDictionary; 24import com.android.inputmethod.latin.PrevWordsInfo; 25import com.android.inputmethod.latin.PrevWordsInfo.WordInfo; 26import com.android.inputmethod.latin.utils.BinaryDictionaryUtils; 27import com.android.inputmethod.latin.utils.DistracterFilter; 28import com.android.inputmethod.latin.utils.FileUtils; 29 30import java.io.File; 31import java.io.FilenameFilter; 32import java.util.ArrayList; 33import java.util.HashSet; 34import java.util.List; 35import java.util.Locale; 36import java.util.Random; 37import java.util.concurrent.TimeUnit; 38 39/** 40 * Unit tests for UserHistoryDictionary 41 */ 42@LargeTest 43public class UserHistoryDictionaryTests extends AndroidTestCase { 44 private static final String TAG = UserHistoryDictionaryTests.class.getSimpleName(); 45 private static final int WAIT_FOR_WRITING_FILE_IN_MILLISECONDS = 3000; 46 private static final String TEST_LOCALE_PREFIX = "test_"; 47 48 private static final String[] CHARACTERS = { 49 "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", 50 "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z" 51 }; 52 53 private int mCurrentTime = 0; 54 55 private void removeAllTestDictFiles() { 56 final Locale dummyLocale = new Locale(TEST_LOCALE_PREFIX); 57 final String dictName = ExpandableBinaryDictionary.getDictName( 58 UserHistoryDictionary.NAME, dummyLocale, null /* dictFile */); 59 final File dictFile = ExpandableBinaryDictionary.getDictFile( 60 mContext, dictName, null /* dictFile */); 61 final FilenameFilter filenameFilter = new FilenameFilter() { 62 @Override 63 public boolean accept(File dir, String filename) { 64 return filename.startsWith(UserHistoryDictionary.NAME + "." + TEST_LOCALE_PREFIX); 65 } 66 }; 67 FileUtils.deleteFilteredFiles(dictFile.getParentFile(), filenameFilter); 68 } 69 70 private void printAllFiles(final File dir) { 71 Log.d(TAG, dir.getAbsolutePath()); 72 for (final File file : dir.listFiles()) { 73 Log.d(TAG, " " + file.getName()); 74 } 75 } 76 77 private void checkExistenceAndRemoveDictFile(final Locale locale, final File dictFile) { 78 Log.d(TAG, "waiting for writing ..."); 79 waitForWriting(locale); 80 if (!dictFile.exists()) { 81 try { 82 Log.d(TAG, dictFile + " is not existing. Wait " 83 + WAIT_FOR_WRITING_FILE_IN_MILLISECONDS + " ms for writing."); 84 printAllFiles(dictFile.getParentFile()); 85 Thread.sleep(WAIT_FOR_WRITING_FILE_IN_MILLISECONDS); 86 } catch (final InterruptedException e) { 87 Log.e(TAG, "Interrupted during waiting for writing the dict file."); 88 } 89 } 90 assertTrue("check exisiting of " + dictFile, dictFile.exists()); 91 FileUtils.deleteRecursively(dictFile); 92 } 93 94 @Override 95 protected void setUp() throws Exception { 96 super.setUp(); 97 resetCurrentTimeForTestMode(); 98 removeAllTestDictFiles(); 99 } 100 101 @Override 102 protected void tearDown() throws Exception { 103 removeAllTestDictFiles(); 104 stopTestModeInNativeCode(); 105 super.tearDown(); 106 } 107 108 private void resetCurrentTimeForTestMode() { 109 mCurrentTime = 0; 110 setCurrentTimeForTestMode(mCurrentTime); 111 } 112 113 private void forcePassingShortTime() { 114 // 3 days. 115 final int timeToElapse = (int)TimeUnit.DAYS.toSeconds(3); 116 mCurrentTime += timeToElapse; 117 setCurrentTimeForTestMode(mCurrentTime); 118 } 119 120 private void forcePassingLongTime() { 121 // 365 days. 122 final int timeToElapse = (int)TimeUnit.DAYS.toSeconds(365); 123 mCurrentTime += timeToElapse; 124 setCurrentTimeForTestMode(mCurrentTime); 125 } 126 127 private static int setCurrentTimeForTestMode(final int currentTime) { 128 return BinaryDictionaryUtils.setCurrentTimeForTest(currentTime); 129 } 130 131 private static int stopTestModeInNativeCode() { 132 return BinaryDictionaryUtils.setCurrentTimeForTest(-1); 133 } 134 135 /** 136 * Generates a random word. 137 */ 138 private static String generateWord(final int value) { 139 final int lengthOfChars = CHARACTERS.length; 140 StringBuilder builder = new StringBuilder(); 141 long lvalue = Math.abs((long)value); 142 while (lvalue > 0) { 143 builder.append(CHARACTERS[(int)(lvalue % lengthOfChars)]); 144 lvalue /= lengthOfChars; 145 } 146 return builder.toString(); 147 } 148 149 private static List<String> generateWords(final int number, final Random random) { 150 final HashSet<String> wordSet = new HashSet<>(); 151 while (wordSet.size() < number) { 152 wordSet.add(generateWord(random.nextInt())); 153 } 154 return new ArrayList<>(wordSet); 155 } 156 157 private static void addToDict(final UserHistoryDictionary dict, final List<String> words) { 158 PrevWordsInfo prevWordsInfo = PrevWordsInfo.EMPTY_PREV_WORDS_INFO; 159 for (String word : words) { 160 UserHistoryDictionary.addToDictionary(dict, prevWordsInfo, word, true, 161 (int)TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis()), 162 DistracterFilter.EMPTY_DISTRACTER_FILTER); 163 prevWordsInfo = prevWordsInfo.getNextPrevWordsInfo(new WordInfo(word)); 164 } 165 } 166 167 /** 168 * @param checkContents if true, checks whether written words are actually in the dictionary 169 * or not. 170 */ 171 private void addAndWriteRandomWords(final Locale locale, final int numberOfWords, 172 final Random random, final boolean checkContents) { 173 final List<String> words = generateWords(numberOfWords, random); 174 final UserHistoryDictionary dict = PersonalizationHelper.getUserHistoryDictionary( 175 mContext, locale); 176 // Add random words to the user history dictionary. 177 addToDict(dict, words); 178 if (checkContents) { 179 dict.waitAllTasksForTests(); 180 for (int i = 0; i < numberOfWords; ++i) { 181 final String word = words.get(i); 182 assertTrue(dict.isInDictionary(word)); 183 } 184 } 185 // write to file. 186 dict.close(); 187 } 188 189 /** 190 * Clear all entries in the user history dictionary. 191 * @param locale dummy locale for testing. 192 */ 193 private void clearHistory(final Locale locale) { 194 final UserHistoryDictionary dict = PersonalizationHelper.getUserHistoryDictionary( 195 mContext, locale); 196 dict.waitAllTasksForTests(); 197 dict.clear(); 198 dict.close(); 199 dict.waitAllTasksForTests(); 200 } 201 202 /** 203 * Shut down executer and wait until all operations of user history are done. 204 * @param locale dummy locale for testing. 205 */ 206 private void waitForWriting(final Locale locale) { 207 final UserHistoryDictionary dict = PersonalizationHelper.getUserHistoryDictionary( 208 mContext, locale); 209 dict.waitAllTasksForTests(); 210 } 211 212 public void testRandomWords() { 213 Log.d(TAG, "This test can be used for profiling."); 214 Log.d(TAG, "Usage: please set UserHistoryDictionary.PROFILE_SAVE_RESTORE to true."); 215 final Locale dummyLocale = 216 new Locale(TEST_LOCALE_PREFIX + "random_words" + System.currentTimeMillis()); 217 final String dictName = ExpandableBinaryDictionary.getDictName( 218 UserHistoryDictionary.NAME, dummyLocale, null /* dictFile */); 219 final File dictFile = ExpandableBinaryDictionary.getDictFile( 220 mContext, dictName, null /* dictFile */); 221 222 final int numberOfWords = 1000; 223 final Random random = new Random(123456); 224 225 try { 226 clearHistory(dummyLocale); 227 addAndWriteRandomWords(dummyLocale, numberOfWords, random, 228 true /* checksContents */); 229 } finally { 230 checkExistenceAndRemoveDictFile(dummyLocale, dictFile); 231 } 232 } 233 234 public void testStressTestForSwitchingLanguagesAndAddingWords() { 235 final int numberOfLanguages = 2; 236 final int numberOfLanguageSwitching = 80; 237 final int numberOfWordsInsertedForEachLanguageSwitch = 100; 238 239 final File dictFiles[] = new File[numberOfLanguages]; 240 final Locale dummyLocales[] = new Locale[numberOfLanguages]; 241 try { 242 final Random random = new Random(123456); 243 244 // Create filename suffixes for this test. 245 for (int i = 0; i < numberOfLanguages; i++) { 246 dummyLocales[i] = new Locale(TEST_LOCALE_PREFIX + "switching_languages" + i); 247 final String dictName = ExpandableBinaryDictionary.getDictName( 248 UserHistoryDictionary.NAME, dummyLocales[i], null /* dictFile */); 249 dictFiles[i] = ExpandableBinaryDictionary.getDictFile( 250 mContext, dictName, null /* dictFile */); 251 clearHistory(dummyLocales[i]); 252 } 253 254 final long start = System.currentTimeMillis(); 255 256 for (int i = 0; i < numberOfLanguageSwitching; i++) { 257 final int index = i % numberOfLanguages; 258 // Switch languages to testFilenameSuffixes[index]. 259 addAndWriteRandomWords(dummyLocales[index], 260 numberOfWordsInsertedForEachLanguageSwitch, random, 261 false /* checksContents */); 262 } 263 264 final long end = System.currentTimeMillis(); 265 Log.d(TAG, "testStressTestForSwitchingLanguageAndAddingWords took " 266 + (end - start) + " ms"); 267 } finally { 268 for (int i = 0; i < numberOfLanguages; i++) { 269 checkExistenceAndRemoveDictFile(dummyLocales[i], dictFiles[i]); 270 } 271 } 272 } 273 274 public void testAddManyWords() { 275 final Locale dummyLocale = 276 new Locale(TEST_LOCALE_PREFIX + "many_random_words" + System.currentTimeMillis()); 277 final String dictName = ExpandableBinaryDictionary.getDictName( 278 UserHistoryDictionary.NAME, dummyLocale, null /* dictFile */); 279 final File dictFile = ExpandableBinaryDictionary.getDictFile( 280 mContext, dictName, null /* dictFile */); 281 final int numberOfWords = 10000; 282 final Random random = new Random(123456); 283 clearHistory(dummyLocale); 284 try { 285 addAndWriteRandomWords(dummyLocale, numberOfWords, random, true /* checksContents */); 286 } finally { 287 checkExistenceAndRemoveDictFile(dummyLocale, dictFile); 288 } 289 } 290 291 public void testDecaying() { 292 final Locale dummyLocale = 293 new Locale(TEST_LOCALE_PREFIX + "decaying" + System.currentTimeMillis()); 294 final int numberOfWords = 5000; 295 final Random random = new Random(123456); 296 resetCurrentTimeForTestMode(); 297 clearHistory(dummyLocale); 298 final List<String> words = generateWords(numberOfWords, random); 299 final UserHistoryDictionary dict = 300 PersonalizationHelper.getUserHistoryDictionary(getContext(), dummyLocale); 301 dict.waitAllTasksForTests(); 302 PrevWordsInfo prevWordsInfo = PrevWordsInfo.EMPTY_PREV_WORDS_INFO; 303 for (final String word : words) { 304 UserHistoryDictionary.addToDictionary(dict, prevWordsInfo, word, true, mCurrentTime, 305 DistracterFilter.EMPTY_DISTRACTER_FILTER); 306 prevWordsInfo = prevWordsInfo.getNextPrevWordsInfo(new WordInfo(word)); 307 dict.waitAllTasksForTests(); 308 assertTrue(dict.isInDictionary(word)); 309 } 310 forcePassingShortTime(); 311 dict.runGCIfRequired(); 312 dict.waitAllTasksForTests(); 313 for (final String word : words) { 314 assertTrue(dict.isInDictionary(word)); 315 } 316 forcePassingLongTime(); 317 dict.runGCIfRequired(); 318 dict.waitAllTasksForTests(); 319 for (final String word : words) { 320 assertFalse(dict.isInDictionary(word)); 321 } 322 } 323} 324