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 
17 package com.android.inputmethod.latin.personalization;
18 
19 import static org.junit.Assert.assertTrue;
20 
21 import android.content.Context;
22 import android.util.Log;
23 
24 import androidx.test.InstrumentationRegistry;
25 import androidx.test.filters.LargeTest;
26 import androidx.test.runner.AndroidJUnit4;
27 
28 import com.android.inputmethod.latin.ExpandableBinaryDictionary;
29 import com.android.inputmethod.latin.utils.BinaryDictionaryUtils;
30 
31 import org.junit.After;
32 import org.junit.Before;
33 import org.junit.Test;
34 import org.junit.runner.RunWith;
35 
36 import java.io.File;
37 import java.util.Locale;
38 import java.util.Random;
39 
40 /**
41  * Unit tests for UserHistoryDictionary
42  */
43 @LargeTest
44 @RunWith(AndroidJUnit4.class)
45 public class UserHistoryDictionaryTests {
46     private static final String TAG = UserHistoryDictionaryTests.class.getSimpleName();
47     private static final int WAIT_FOR_WRITING_FILE_IN_MILLISECONDS = 3000;
48     private static final String TEST_ACCOUNT = "account@example.com";
49 
50     private int mCurrentTime = 0;
51 
getContext()52     private Context getContext() {
53         return InstrumentationRegistry.getTargetContext();
54     }
55 
printAllFiles(final File dir)56     private static void printAllFiles(final File dir) {
57         Log.d(TAG, dir.getAbsolutePath());
58         for (final File file : dir.listFiles()) {
59             Log.d(TAG, "  " + file.getName());
60         }
61     }
62 
assertDictionaryExists(final UserHistoryDictionary dict, final File dictFile)63     private static void assertDictionaryExists(final UserHistoryDictionary dict,
64             final File dictFile) {
65         Log.d(TAG, "waiting for writing ...");
66         dict.waitAllTasksForTests();
67         if (!dictFile.exists()) {
68             try {
69                 Log.d(TAG, dictFile + " is not existing. Wait "
70                         + WAIT_FOR_WRITING_FILE_IN_MILLISECONDS + " ms for writing.");
71                 printAllFiles(dictFile.getParentFile());
72                 Thread.sleep(WAIT_FOR_WRITING_FILE_IN_MILLISECONDS);
73             } catch (final InterruptedException e) {
74                 Log.e(TAG, "Interrupted during waiting for writing the dict file.");
75             }
76         }
77         assertTrue("Following dictionary file doesn't exist: " + dictFile, dictFile.exists());
78     }
79 
80     @Before
setUp()81     public void setUp() throws Exception {
82         resetCurrentTimeForTestMode();
83         UserHistoryDictionaryTestsHelper.removeAllTestDictFiles(
84                 UserHistoryDictionaryTestsHelper.TEST_LOCALE_PREFIX, getContext());
85     }
86 
87     @After
tearDown()88     public void tearDown() throws Exception {
89         UserHistoryDictionaryTestsHelper.removeAllTestDictFiles(
90                 UserHistoryDictionaryTestsHelper.TEST_LOCALE_PREFIX, getContext());
91         stopTestModeInNativeCode();
92     }
93 
resetCurrentTimeForTestMode()94     private void resetCurrentTimeForTestMode() {
95         mCurrentTime = 0;
96         setCurrentTimeForTestMode(mCurrentTime);
97     }
98 
setCurrentTimeForTestMode(final int currentTime)99     private static int setCurrentTimeForTestMode(final int currentTime) {
100         return BinaryDictionaryUtils.setCurrentTimeForTest(currentTime);
101     }
102 
stopTestModeInNativeCode()103     private static int stopTestModeInNativeCode() {
104         return BinaryDictionaryUtils.setCurrentTimeForTest(-1);
105     }
106 
107     /**
108      * Clear all entries in the user history dictionary.
109      * @param dict the user history dictionary.
110      */
clearHistory(final UserHistoryDictionary dict)111     private static void clearHistory(final UserHistoryDictionary dict) {
112         dict.waitAllTasksForTests();
113         dict.clear();
114         dict.close();
115         dict.waitAllTasksForTests();
116     }
117 
doTestRandomWords(final String testAccount)118     private void doTestRandomWords(final String testAccount) {
119         Log.d(TAG, "This test can be used for profiling.");
120         Log.d(TAG, "Usage: please set UserHistoryDictionary.PROFILE_SAVE_RESTORE to true.");
121         final Locale fakeLocale = UserHistoryDictionaryTestsHelper.getFakeLocale("random_words");
122         final String dictName = UserHistoryDictionary.getUserHistoryDictName(
123                 UserHistoryDictionary.NAME, fakeLocale,
124                 null /* dictFile */,
125                 testAccount /* account */);
126         final File dictFile = ExpandableBinaryDictionary.getDictFile(
127                 getContext(), dictName, null /* dictFile */);
128         final UserHistoryDictionary dict = PersonalizationHelper.getUserHistoryDictionary(
129                 getContext(), fakeLocale, testAccount);
130         clearHistory(dict);
131 
132         final int numberOfWords = 1000;
133         final Random random = new Random(123456);
134         assertTrue(UserHistoryDictionaryTestsHelper.addAndWriteRandomWords(
135                 dict, numberOfWords, random, true /* checksContents */, mCurrentTime));
136         assertDictionaryExists(dict, dictFile);
137     }
138 
139     @Test
testRandomWords_NullAccount()140     public void testRandomWords_NullAccount() {
141         doTestRandomWords(null /* testAccount */);
142     }
143 
144     @Test
testRandomWords()145     public void testRandomWords() {
146         doTestRandomWords(TEST_ACCOUNT);
147     }
148 
149     @Test
testStressTestForSwitchingLanguagesAndAddingWords()150     public void testStressTestForSwitchingLanguagesAndAddingWords() {
151         doTestStressTestForSwitchingLanguagesAndAddingWords(TEST_ACCOUNT);
152     }
153 
154     @Test
testStressTestForSwitchingLanguagesAndAddingWords_NullAccount()155     public void testStressTestForSwitchingLanguagesAndAddingWords_NullAccount() {
156         doTestStressTestForSwitchingLanguagesAndAddingWords(null /* testAccount */);
157     }
158 
doTestStressTestForSwitchingLanguagesAndAddingWords(final String testAccount)159     private void doTestStressTestForSwitchingLanguagesAndAddingWords(final String testAccount) {
160         final int numberOfLanguages = 2;
161         final int numberOfLanguageSwitching = 80;
162         final int numberOfWordsInsertedForEachLanguageSwitch = 100;
163 
164         final File dictFiles[] = new File[numberOfLanguages];
165         final UserHistoryDictionary dicts[] = new UserHistoryDictionary[numberOfLanguages];
166 
167         try {
168             final Random random = new Random(123456);
169 
170             // Create filename suffixes for this test.
171             for (int i = 0; i < numberOfLanguages; i++) {
172                 final Locale fakeLocale =
173                         UserHistoryDictionaryTestsHelper.getFakeLocale("switching_languages" + i);
174                 final String dictName = UserHistoryDictionary.getUserHistoryDictName(
175                         UserHistoryDictionary.NAME, fakeLocale, null /* dictFile */,
176                         testAccount /* account */);
177                 dictFiles[i] = ExpandableBinaryDictionary.getDictFile(
178                         getContext(), dictName, null /* dictFile */);
179                 dicts[i] = PersonalizationHelper.getUserHistoryDictionary(getContext(),
180                         fakeLocale, testAccount);
181                 clearHistory(dicts[i]);
182             }
183 
184             final long start = System.currentTimeMillis();
185 
186             for (int i = 0; i < numberOfLanguageSwitching; i++) {
187                 final int index = i % numberOfLanguages;
188                 // Switch to dicts[index].
189                 assertTrue(UserHistoryDictionaryTestsHelper.addAndWriteRandomWords(dicts[index],
190                         numberOfWordsInsertedForEachLanguageSwitch,
191                         random,
192                         false /* checksContents */,
193                         mCurrentTime));
194             }
195 
196             final long end = System.currentTimeMillis();
197             Log.d(TAG, "testStressTestForSwitchingLanguageAndAddingWords took "
198                     + (end - start) + " ms");
199         } finally {
200             for (int i = 0; i < numberOfLanguages; i++) {
201                 assertDictionaryExists(dicts[i], dictFiles[i]);
202             }
203         }
204     }
205 
206     @Test
testAddManyWords()207     public void testAddManyWords() {
208         doTestAddManyWords(TEST_ACCOUNT);
209     }
210 
211     @Test
testAddManyWords_NullAccount()212     public void testAddManyWords_NullAccount() {
213         doTestAddManyWords(null /* testAccount */);
214     }
215 
doTestAddManyWords(final String testAccount)216     private void doTestAddManyWords(final String testAccount) {
217         final Locale fakeLocale =
218                 UserHistoryDictionaryTestsHelper.getFakeLocale("many_random_words");
219         final String dictName = UserHistoryDictionary.getUserHistoryDictName(
220                 UserHistoryDictionary.NAME, fakeLocale, null /* dictFile */, testAccount);
221         final File dictFile = ExpandableBinaryDictionary.getDictFile(
222                 getContext(), dictName, null /* dictFile */);
223         final int numberOfWords = 10000;
224         final Random random = new Random(123456);
225         final UserHistoryDictionary dict = PersonalizationHelper.getUserHistoryDictionary(
226                 getContext(), fakeLocale, testAccount);
227         clearHistory(dict);
228         assertTrue(UserHistoryDictionaryTestsHelper.addAndWriteRandomWords(dict,
229                 numberOfWords, random, true /* checksContents */, mCurrentTime));
230         assertDictionaryExists(dict, dictFile);
231     }
232 }
233