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