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