UserHistoryDictionaryTests.java revision d80072c4b819a441c6d22a96d158d7cf4d5383b2
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 UserHistoryDictionary dict, 78 final File dictFile) { 79 Log.d(TAG, "waiting for writing ..."); 80 dict.waitAllTasksForTests(); 81 if (!dictFile.exists()) { 82 try { 83 Log.d(TAG, dictFile + " is not existing. Wait " 84 + WAIT_FOR_WRITING_FILE_IN_MILLISECONDS + " ms for writing."); 85 printAllFiles(dictFile.getParentFile()); 86 Thread.sleep(WAIT_FOR_WRITING_FILE_IN_MILLISECONDS); 87 } catch (final InterruptedException e) { 88 Log.e(TAG, "Interrupted during waiting for writing the dict file."); 89 } 90 } 91 assertTrue("check exisiting of " + dictFile, dictFile.exists()); 92 FileUtils.deleteRecursively(dictFile); 93 } 94 95 private static Locale getDummyLocale(final String name) { 96 return new Locale(TEST_LOCALE_PREFIX + name + System.currentTimeMillis()); 97 } 98 99 @Override 100 protected void setUp() throws Exception { 101 super.setUp(); 102 resetCurrentTimeForTestMode(); 103 removeAllTestDictFiles(); 104 } 105 106 @Override 107 protected void tearDown() throws Exception { 108 removeAllTestDictFiles(); 109 stopTestModeInNativeCode(); 110 super.tearDown(); 111 } 112 113 private void resetCurrentTimeForTestMode() { 114 mCurrentTime = 0; 115 setCurrentTimeForTestMode(mCurrentTime); 116 } 117 118 private void forcePassingShortTime() { 119 // 3 days. 120 final int timeToElapse = (int)TimeUnit.DAYS.toSeconds(3); 121 mCurrentTime += timeToElapse; 122 setCurrentTimeForTestMode(mCurrentTime); 123 } 124 125 private void forcePassingLongTime() { 126 // 365 days. 127 final int timeToElapse = (int)TimeUnit.DAYS.toSeconds(365); 128 mCurrentTime += timeToElapse; 129 setCurrentTimeForTestMode(mCurrentTime); 130 } 131 132 private static int setCurrentTimeForTestMode(final int currentTime) { 133 return BinaryDictionaryUtils.setCurrentTimeForTest(currentTime); 134 } 135 136 private static int stopTestModeInNativeCode() { 137 return BinaryDictionaryUtils.setCurrentTimeForTest(-1); 138 } 139 140 /** 141 * Generates a random word. 142 */ 143 private static String generateWord(final int value) { 144 final int lengthOfChars = CHARACTERS.length; 145 StringBuilder builder = new StringBuilder(); 146 long lvalue = Math.abs((long)value); 147 while (lvalue > 0) { 148 builder.append(CHARACTERS[(int)(lvalue % lengthOfChars)]); 149 lvalue /= lengthOfChars; 150 } 151 return builder.toString(); 152 } 153 154 private static List<String> generateWords(final int number, final Random random) { 155 final HashSet<String> wordSet = new HashSet<>(); 156 while (wordSet.size() < number) { 157 wordSet.add(generateWord(random.nextInt())); 158 } 159 return new ArrayList<>(wordSet); 160 } 161 162 private static void addToDict(final UserHistoryDictionary dict, final List<String> words) { 163 PrevWordsInfo prevWordsInfo = PrevWordsInfo.EMPTY_PREV_WORDS_INFO; 164 for (String word : words) { 165 UserHistoryDictionary.addToDictionary(dict, prevWordsInfo, word, true, 166 (int)TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis()), 167 DistracterFilter.EMPTY_DISTRACTER_FILTER); 168 prevWordsInfo = prevWordsInfo.getNextPrevWordsInfo(new WordInfo(word)); 169 } 170 } 171 172 /** 173 * @param checkContents if true, checks whether written words are actually in the dictionary 174 * or not. 175 */ 176 private void addAndWriteRandomWords(final UserHistoryDictionary dict, 177 final int numberOfWords, final Random random, final boolean checkContents) { 178 final List<String> words = generateWords(numberOfWords, random); 179 // Add random words to the user history dictionary. 180 addToDict(dict, words); 181 if (checkContents) { 182 dict.waitAllTasksForTests(); 183 for (int i = 0; i < numberOfWords; ++i) { 184 final String word = words.get(i); 185 assertTrue(dict.isInDictionary(word)); 186 } 187 } 188 // write to file. 189 dict.close(); 190 } 191 192 /** 193 * Clear all entries in the user history dictionary. 194 * @param dict the user history dictionary. 195 */ 196 private void clearHistory(final UserHistoryDictionary dict) { 197 dict.waitAllTasksForTests(); 198 dict.clear(); 199 dict.close(); 200 dict.waitAllTasksForTests(); 201 } 202 203 public void testRandomWords() { 204 Log.d(TAG, "This test can be used for profiling."); 205 Log.d(TAG, "Usage: please set UserHistoryDictionary.PROFILE_SAVE_RESTORE to true."); 206 final Locale dummyLocale = getDummyLocale("random_words"); 207 final String dictName = ExpandableBinaryDictionary.getDictName( 208 UserHistoryDictionary.NAME, dummyLocale, null /* dictFile */); 209 final File dictFile = ExpandableBinaryDictionary.getDictFile( 210 mContext, dictName, null /* dictFile */); 211 final UserHistoryDictionary dict = PersonalizationHelper.getUserHistoryDictionary( 212 getContext(), dummyLocale); 213 214 final int numberOfWords = 1000; 215 final Random random = new Random(123456); 216 217 try { 218 clearHistory(dict); 219 addAndWriteRandomWords(dict, numberOfWords, random, true /* checksContents */); 220 } finally { 221 checkExistenceAndRemoveDictFile(dict, dictFile); 222 } 223 } 224 225 public void testStressTestForSwitchingLanguagesAndAddingWords() { 226 final int numberOfLanguages = 2; 227 final int numberOfLanguageSwitching = 80; 228 final int numberOfWordsInsertedForEachLanguageSwitch = 100; 229 230 final File dictFiles[] = new File[numberOfLanguages]; 231 final UserHistoryDictionary dicts[] = new UserHistoryDictionary[numberOfLanguages]; 232 233 try { 234 final Random random = new Random(123456); 235 236 // Create filename suffixes for this test. 237 for (int i = 0; i < numberOfLanguages; i++) { 238 final Locale dummyLocale = getDummyLocale("switching_languages" + i); 239 final String dictName = ExpandableBinaryDictionary.getDictName( 240 UserHistoryDictionary.NAME, dummyLocale, null /* dictFile */); 241 dictFiles[i] = ExpandableBinaryDictionary.getDictFile( 242 mContext, dictName, null /* dictFile */); 243 dicts[i] = PersonalizationHelper.getUserHistoryDictionary(getContext(), 244 dummyLocale); 245 clearHistory(dicts[i]); 246 } 247 248 final long start = System.currentTimeMillis(); 249 250 for (int i = 0; i < numberOfLanguageSwitching; i++) { 251 final int index = i % numberOfLanguages; 252 // Switch to dicts[index]. 253 addAndWriteRandomWords(dicts[index], numberOfWordsInsertedForEachLanguageSwitch, 254 random, false /* checksContents */); 255 } 256 257 final long end = System.currentTimeMillis(); 258 Log.d(TAG, "testStressTestForSwitchingLanguageAndAddingWords took " 259 + (end - start) + " ms"); 260 } finally { 261 for (int i = 0; i < numberOfLanguages; i++) { 262 checkExistenceAndRemoveDictFile(dicts[i], dictFiles[i]); 263 } 264 } 265 } 266 267 public void testAddManyWords() { 268 final Locale dummyLocale = getDummyLocale("many_random_words"); 269 final String dictName = ExpandableBinaryDictionary.getDictName( 270 UserHistoryDictionary.NAME, dummyLocale, null /* dictFile */); 271 final File dictFile = ExpandableBinaryDictionary.getDictFile( 272 mContext, dictName, null /* dictFile */); 273 final int numberOfWords = 10000; 274 final Random random = new Random(123456); 275 final UserHistoryDictionary dict = PersonalizationHelper.getUserHistoryDictionary( 276 getContext(), dummyLocale); 277 clearHistory(dict); 278 try { 279 addAndWriteRandomWords(dict, numberOfWords, random, true /* checksContents */); 280 } finally { 281 checkExistenceAndRemoveDictFile(dict, dictFile); 282 } 283 } 284 285 public void testDecaying() { 286 final Locale dummyLocale = getDummyLocale("decaying"); 287 final UserHistoryDictionary dict = PersonalizationHelper.getUserHistoryDictionary( 288 getContext(), dummyLocale); 289 final int numberOfWords = 5000; 290 final Random random = new Random(123456); 291 resetCurrentTimeForTestMode(); 292 clearHistory(dict); 293 final List<String> words = generateWords(numberOfWords, random); 294 dict.waitAllTasksForTests(); 295 PrevWordsInfo prevWordsInfo = PrevWordsInfo.EMPTY_PREV_WORDS_INFO; 296 for (final String word : words) { 297 UserHistoryDictionary.addToDictionary(dict, prevWordsInfo, word, true, mCurrentTime, 298 DistracterFilter.EMPTY_DISTRACTER_FILTER); 299 prevWordsInfo = prevWordsInfo.getNextPrevWordsInfo(new WordInfo(word)); 300 dict.waitAllTasksForTests(); 301 assertTrue(dict.isInDictionary(word)); 302 } 303 forcePassingShortTime(); 304 dict.runGCIfRequired(); 305 dict.waitAllTasksForTests(); 306 for (final String word : words) { 307 assertTrue(dict.isInDictionary(word)); 308 } 309 forcePassingLongTime(); 310 dict.runGCIfRequired(); 311 dict.waitAllTasksForTests(); 312 for (final String word : words) { 313 assertFalse(dict.isInDictionary(word)); 314 } 315 } 316} 317