UserHistoryDictionaryTests.java revision e5a35711b854aedeeea2f45105b941b9deee49bc
184d858ed5e187eb9d4b56b593e1d9287f762bbcaYuichiro Hanada/*
284d858ed5e187eb9d4b56b593e1d9287f762bbcaYuichiro Hanada * Copyright (C) 2012 The Android Open Source Project
384d858ed5e187eb9d4b56b593e1d9287f762bbcaYuichiro Hanada *
484d858ed5e187eb9d4b56b593e1d9287f762bbcaYuichiro Hanada * Licensed under the Apache License, Version 2.0 (the "License");
584d858ed5e187eb9d4b56b593e1d9287f762bbcaYuichiro Hanada * you may not use this file except in compliance with the License.
684d858ed5e187eb9d4b56b593e1d9287f762bbcaYuichiro Hanada * You may obtain a copy of the License at
784d858ed5e187eb9d4b56b593e1d9287f762bbcaYuichiro Hanada *
884d858ed5e187eb9d4b56b593e1d9287f762bbcaYuichiro Hanada *      http://www.apache.org/licenses/LICENSE-2.0
984d858ed5e187eb9d4b56b593e1d9287f762bbcaYuichiro Hanada *
1084d858ed5e187eb9d4b56b593e1d9287f762bbcaYuichiro Hanada * Unless required by applicable law or agreed to in writing, software
1184d858ed5e187eb9d4b56b593e1d9287f762bbcaYuichiro Hanada * distributed under the License is distributed on an "AS IS" BASIS,
1284d858ed5e187eb9d4b56b593e1d9287f762bbcaYuichiro Hanada * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1384d858ed5e187eb9d4b56b593e1d9287f762bbcaYuichiro Hanada * See the License for the specific language governing permissions and
1484d858ed5e187eb9d4b56b593e1d9287f762bbcaYuichiro Hanada * limitations under the License.
1584d858ed5e187eb9d4b56b593e1d9287f762bbcaYuichiro Hanada */
1684d858ed5e187eb9d4b56b593e1d9287f762bbcaYuichiro Hanada
17ffcbbaf12788a9fc9398607a548e552d7d2bf05eSatoshi Kataokapackage com.android.inputmethod.latin.personalization;
1884d858ed5e187eb9d4b56b593e1d9287f762bbcaYuichiro Hanada
1984d858ed5e187eb9d4b56b593e1d9287f762bbcaYuichiro Hanadaimport android.content.SharedPreferences;
2084d858ed5e187eb9d4b56b593e1d9287f762bbcaYuichiro Hanadaimport android.preference.PreferenceManager;
2184d858ed5e187eb9d4b56b593e1d9287f762bbcaYuichiro Hanadaimport android.test.AndroidTestCase;
22b4598f7d05d6afd01ddc7ea0bed71dda837d1debTadashi G. Takaokaimport android.test.suitebuilder.annotation.LargeTest;
2384d858ed5e187eb9d4b56b593e1d9287f762bbcaYuichiro Hanadaimport android.util.Log;
2484d858ed5e187eb9d4b56b593e1d9287f762bbcaYuichiro Hanada
258aaae56cf6694ec75043be56f1c7812a343b24d5Yuichiro Hanadaimport com.android.inputmethod.latin.ExpandableBinaryDictionary;
26e28eba5074664d5716b8e58b8d0a235746b261ebKen Wakasaimport com.android.inputmethod.latin.utils.CollectionUtils;
27e28eba5074664d5716b8e58b8d0a235746b261ebKen Wakasa
286def28d1dacb0f02c08d91c8be3ed877624f74abYuichiro Hanadaimport java.io.File;
2984d858ed5e187eb9d4b56b593e1d9287f762bbcaYuichiro Hanadaimport java.util.ArrayList;
3084d858ed5e187eb9d4b56b593e1d9287f762bbcaYuichiro Hanadaimport java.util.List;
3184d858ed5e187eb9d4b56b593e1d9287f762bbcaYuichiro Hanadaimport java.util.Random;
3284d858ed5e187eb9d4b56b593e1d9287f762bbcaYuichiro Hanadaimport java.util.Set;
330d70bcc821c22f7001b66f4c7b83842661b8391eYuichiro Hanadaimport java.util.concurrent.TimeUnit;
3484d858ed5e187eb9d4b56b593e1d9287f762bbcaYuichiro Hanada
3584d858ed5e187eb9d4b56b593e1d9287f762bbcaYuichiro Hanada/**
3684d858ed5e187eb9d4b56b593e1d9287f762bbcaYuichiro Hanada * Unit tests for UserHistoryDictionary
3784d858ed5e187eb9d4b56b593e1d9287f762bbcaYuichiro Hanada */
38b4598f7d05d6afd01ddc7ea0bed71dda837d1debTadashi G. Takaoka@LargeTest
3984d858ed5e187eb9d4b56b593e1d9287f762bbcaYuichiro Hanadapublic class UserHistoryDictionaryTests extends AndroidTestCase {
4084d858ed5e187eb9d4b56b593e1d9287f762bbcaYuichiro Hanada    private static final String TAG = UserHistoryDictionaryTests.class.getSimpleName();
4184d858ed5e187eb9d4b56b593e1d9287f762bbcaYuichiro Hanada    private SharedPreferences mPrefs;
4284d858ed5e187eb9d4b56b593e1d9287f762bbcaYuichiro Hanada
4384d858ed5e187eb9d4b56b593e1d9287f762bbcaYuichiro Hanada    private static final String[] CHARACTERS = {
4484d858ed5e187eb9d4b56b593e1d9287f762bbcaYuichiro Hanada        "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m",
4584d858ed5e187eb9d4b56b593e1d9287f762bbcaYuichiro Hanada        "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"
4684d858ed5e187eb9d4b56b593e1d9287f762bbcaYuichiro Hanada    };
4784d858ed5e187eb9d4b56b593e1d9287f762bbcaYuichiro Hanada
480d70bcc821c22f7001b66f4c7b83842661b8391eYuichiro Hanada    private static final int MIN_USER_HISTORY_DICTIONARY_FILE_SIZE = 1000;
490d70bcc821c22f7001b66f4c7b83842661b8391eYuichiro Hanada
5084d858ed5e187eb9d4b56b593e1d9287f762bbcaYuichiro Hanada    @Override
5184d858ed5e187eb9d4b56b593e1d9287f762bbcaYuichiro Hanada    public void setUp() {
5284d858ed5e187eb9d4b56b593e1d9287f762bbcaYuichiro Hanada        mPrefs = PreferenceManager.getDefaultSharedPreferences(getContext());
5384d858ed5e187eb9d4b56b593e1d9287f762bbcaYuichiro Hanada    }
5484d858ed5e187eb9d4b56b593e1d9287f762bbcaYuichiro Hanada
5584d858ed5e187eb9d4b56b593e1d9287f762bbcaYuichiro Hanada    /**
5684d858ed5e187eb9d4b56b593e1d9287f762bbcaYuichiro Hanada     * Generates a random word.
5784d858ed5e187eb9d4b56b593e1d9287f762bbcaYuichiro Hanada     */
5884d858ed5e187eb9d4b56b593e1d9287f762bbcaYuichiro Hanada    private String generateWord(final int value) {
5984d858ed5e187eb9d4b56b593e1d9287f762bbcaYuichiro Hanada        final int lengthOfChars = CHARACTERS.length;
6084d858ed5e187eb9d4b56b593e1d9287f762bbcaYuichiro Hanada        StringBuilder builder = new StringBuilder();
6184d858ed5e187eb9d4b56b593e1d9287f762bbcaYuichiro Hanada        long lvalue = Math.abs((long)value);
6284d858ed5e187eb9d4b56b593e1d9287f762bbcaYuichiro Hanada        while (lvalue > 0) {
6384d858ed5e187eb9d4b56b593e1d9287f762bbcaYuichiro Hanada            builder.append(CHARACTERS[(int)(lvalue % lengthOfChars)]);
6484d858ed5e187eb9d4b56b593e1d9287f762bbcaYuichiro Hanada            lvalue /= lengthOfChars;
6584d858ed5e187eb9d4b56b593e1d9287f762bbcaYuichiro Hanada        }
6684d858ed5e187eb9d4b56b593e1d9287f762bbcaYuichiro Hanada        return builder.toString();
6784d858ed5e187eb9d4b56b593e1d9287f762bbcaYuichiro Hanada    }
6884d858ed5e187eb9d4b56b593e1d9287f762bbcaYuichiro Hanada
6984d858ed5e187eb9d4b56b593e1d9287f762bbcaYuichiro Hanada    private List<String> generateWords(final int number, final Random random) {
7084d858ed5e187eb9d4b56b593e1d9287f762bbcaYuichiro Hanada        final Set<String> wordSet = CollectionUtils.newHashSet();
7184d858ed5e187eb9d4b56b593e1d9287f762bbcaYuichiro Hanada        while (wordSet.size() < number) {
7284d858ed5e187eb9d4b56b593e1d9287f762bbcaYuichiro Hanada            wordSet.add(generateWord(random.nextInt()));
7384d858ed5e187eb9d4b56b593e1d9287f762bbcaYuichiro Hanada        }
7484d858ed5e187eb9d4b56b593e1d9287f762bbcaYuichiro Hanada        return new ArrayList<String>(wordSet);
7584d858ed5e187eb9d4b56b593e1d9287f762bbcaYuichiro Hanada    }
7684d858ed5e187eb9d4b56b593e1d9287f762bbcaYuichiro Hanada
7787d06afc66db68f0b30b36593095511314793517Satoshi Kataoka    private void addToDict(final UserHistoryPredictionDictionary dict, final List<String> words) {
7884d858ed5e187eb9d4b56b593e1d9287f762bbcaYuichiro Hanada        String prevWord = null;
7984d858ed5e187eb9d4b56b593e1d9287f762bbcaYuichiro Hanada        for (String word : words) {
8084d858ed5e187eb9d4b56b593e1d9287f762bbcaYuichiro Hanada            dict.forceAddWordForTest(prevWord, word, true);
8184d858ed5e187eb9d4b56b593e1d9287f762bbcaYuichiro Hanada            prevWord = word;
8284d858ed5e187eb9d4b56b593e1d9287f762bbcaYuichiro Hanada        }
8384d858ed5e187eb9d4b56b593e1d9287f762bbcaYuichiro Hanada    }
8484d858ed5e187eb9d4b56b593e1d9287f762bbcaYuichiro Hanada
85e5a35711b854aedeeea2f45105b941b9deee49bcSatoshi Kataoka    /**
86e5a35711b854aedeeea2f45105b941b9deee49bcSatoshi Kataoka     * @param checksContents if true, checks whether written words are actually in the dictionary
87e5a35711b854aedeeea2f45105b941b9deee49bcSatoshi Kataoka     * or not.
88e5a35711b854aedeeea2f45105b941b9deee49bcSatoshi Kataoka     */
898aaae56cf6694ec75043be56f1c7812a343b24d5Yuichiro Hanada    private void addAndWriteRandomWords(final String testFilenameSuffix, final int numberOfWords,
90e5a35711b854aedeeea2f45105b941b9deee49bcSatoshi Kataoka            final Random random, final boolean checksContents) {
910d70bcc821c22f7001b66f4c7b83842661b8391eYuichiro Hanada        final List<String> words = generateWords(numberOfWords, random);
920d70bcc821c22f7001b66f4c7b83842661b8391eYuichiro Hanada        final UserHistoryPredictionDictionary dict =
936e04d6593239e841f5dac0d3f32d613967c11e22Keisuke Kuroyanagi                PersonalizationHelper.getUserHistoryPredictionDictionary(getContext(),
948aaae56cf6694ec75043be56f1c7812a343b24d5Yuichiro Hanada                        testFilenameSuffix /* locale */, mPrefs);
950d70bcc821c22f7001b66f4c7b83842661b8391eYuichiro Hanada        // Add random words to the user history dictionary.
960d70bcc821c22f7001b66f4c7b83842661b8391eYuichiro Hanada        addToDict(dict, words);
97e5a35711b854aedeeea2f45105b941b9deee49bcSatoshi Kataoka        if (checksContents) {
98e5a35711b854aedeeea2f45105b941b9deee49bcSatoshi Kataoka            try {
99e5a35711b854aedeeea2f45105b941b9deee49bcSatoshi Kataoka                Thread.sleep(TimeUnit.MILLISECONDS.convert(5L, TimeUnit.SECONDS));
100e5a35711b854aedeeea2f45105b941b9deee49bcSatoshi Kataoka            } catch (InterruptedException e) {
101e5a35711b854aedeeea2f45105b941b9deee49bcSatoshi Kataoka            }
102e5a35711b854aedeeea2f45105b941b9deee49bcSatoshi Kataoka            for (int i = 0; i < 10 && i < numberOfWords; ++i) {
103e5a35711b854aedeeea2f45105b941b9deee49bcSatoshi Kataoka                final String word = words.get(i);
104e5a35711b854aedeeea2f45105b941b9deee49bcSatoshi Kataoka                // This may fail as long as we use tryLock on inserting the bigram words
105e5a35711b854aedeeea2f45105b941b9deee49bcSatoshi Kataoka                assertTrue(dict.isInDictionaryForTests(word));
106e5a35711b854aedeeea2f45105b941b9deee49bcSatoshi Kataoka            }
107e5a35711b854aedeeea2f45105b941b9deee49bcSatoshi Kataoka        }
1080d70bcc821c22f7001b66f4c7b83842661b8391eYuichiro Hanada        // write to file.
1090d70bcc821c22f7001b66f4c7b83842661b8391eYuichiro Hanada        dict.close();
1100d70bcc821c22f7001b66f4c7b83842661b8391eYuichiro Hanada    }
1110d70bcc821c22f7001b66f4c7b83842661b8391eYuichiro Hanada
11284d858ed5e187eb9d4b56b593e1d9287f762bbcaYuichiro Hanada    public void testRandomWords() {
1136def28d1dacb0f02c08d91c8be3ed877624f74abYuichiro Hanada        File dictFile = null;
1140d70bcc821c22f7001b66f4c7b83842661b8391eYuichiro Hanada        Log.d(TAG, "This test can be used for profiling.");
1150d70bcc821c22f7001b66f4c7b83842661b8391eYuichiro Hanada        Log.d(TAG, "Usage: please set UserHistoryDictionary.PROFILE_SAVE_RESTORE to true.");
1168aaae56cf6694ec75043be56f1c7812a343b24d5Yuichiro Hanada        final String testFilenameSuffix = "testRandomWords" + System.currentTimeMillis();
1170d70bcc821c22f7001b66f4c7b83842661b8391eYuichiro Hanada        final int numberOfWords = 1000;
1180d70bcc821c22f7001b66f4c7b83842661b8391eYuichiro Hanada        final Random random = new Random(123456);
1196def28d1dacb0f02c08d91c8be3ed877624f74abYuichiro Hanada
1200d70bcc821c22f7001b66f4c7b83842661b8391eYuichiro Hanada        try {
121e5a35711b854aedeeea2f45105b941b9deee49bcSatoshi Kataoka            addAndWriteRandomWords(testFilenameSuffix, numberOfWords, random,
122e5a35711b854aedeeea2f45105b941b9deee49bcSatoshi Kataoka                    true /* checksContents */);
1230d70bcc821c22f7001b66f4c7b83842661b8391eYuichiro Hanada        } finally {
1246def28d1dacb0f02c08d91c8be3ed877624f74abYuichiro Hanada            try {
1250d70bcc821c22f7001b66f4c7b83842661b8391eYuichiro Hanada                Log.d(TAG, "waiting for writing ...");
1260d70bcc821c22f7001b66f4c7b83842661b8391eYuichiro Hanada                Thread.sleep(TimeUnit.MILLISECONDS.convert(5L, TimeUnit.SECONDS));
1276def28d1dacb0f02c08d91c8be3ed877624f74abYuichiro Hanada            } catch (InterruptedException e) {
1286def28d1dacb0f02c08d91c8be3ed877624f74abYuichiro Hanada                Log.d(TAG, "InterruptedException: " + e);
1296def28d1dacb0f02c08d91c8be3ed877624f74abYuichiro Hanada            }
1306def28d1dacb0f02c08d91c8be3ed877624f74abYuichiro Hanada
1318aaae56cf6694ec75043be56f1c7812a343b24d5Yuichiro Hanada            final String fileName = UserHistoryPredictionDictionary.NAME + "." + testFilenameSuffix
1328aaae56cf6694ec75043be56f1c7812a343b24d5Yuichiro Hanada                    + ExpandableBinaryDictionary.DICT_FILE_EXTENSION;
1330d70bcc821c22f7001b66f4c7b83842661b8391eYuichiro Hanada            dictFile = new File(getContext().getFilesDir(), fileName);
1346def28d1dacb0f02c08d91c8be3ed877624f74abYuichiro Hanada
1356def28d1dacb0f02c08d91c8be3ed877624f74abYuichiro Hanada            if (dictFile != null) {
1360d70bcc821c22f7001b66f4c7b83842661b8391eYuichiro Hanada                assertTrue(dictFile.exists());
1370d70bcc821c22f7001b66f4c7b83842661b8391eYuichiro Hanada                assertTrue(dictFile.length() >= MIN_USER_HISTORY_DICTIONARY_FILE_SIZE);
1386def28d1dacb0f02c08d91c8be3ed877624f74abYuichiro Hanada                dictFile.delete();
1396def28d1dacb0f02c08d91c8be3ed877624f74abYuichiro Hanada            }
14084d858ed5e187eb9d4b56b593e1d9287f762bbcaYuichiro Hanada        }
14184d858ed5e187eb9d4b56b593e1d9287f762bbcaYuichiro Hanada    }
14228a70b63c5748783c3b6fcac551cb69852840474Keisuke Kuroynagi
14328a70b63c5748783c3b6fcac551cb69852840474Keisuke Kuroynagi    public void testStressTestForSwitchingLanguagesAndAddingWords() {
14428a70b63c5748783c3b6fcac551cb69852840474Keisuke Kuroynagi        final int numberOfLanguages = 2;
1450d70bcc821c22f7001b66f4c7b83842661b8391eYuichiro Hanada        final int numberOfLanguageSwitching = 80;
1460d70bcc821c22f7001b66f4c7b83842661b8391eYuichiro Hanada        final int numberOfWordsInsertedForEachLanguageSwitch = 100;
14728a70b63c5748783c3b6fcac551cb69852840474Keisuke Kuroynagi
14828a70b63c5748783c3b6fcac551cb69852840474Keisuke Kuroynagi        final File dictFiles[] = new File[numberOfLanguages];
14928a70b63c5748783c3b6fcac551cb69852840474Keisuke Kuroynagi        try {
15028a70b63c5748783c3b6fcac551cb69852840474Keisuke Kuroynagi            final Random random = new Random(123456);
15128a70b63c5748783c3b6fcac551cb69852840474Keisuke Kuroynagi
1520d70bcc821c22f7001b66f4c7b83842661b8391eYuichiro Hanada            // Create filename suffixes for this test.
1530d70bcc821c22f7001b66f4c7b83842661b8391eYuichiro Hanada            String testFilenameSuffixes[] = new String[numberOfLanguages];
15428a70b63c5748783c3b6fcac551cb69852840474Keisuke Kuroynagi            for (int i = 0; i < numberOfLanguages; i++) {
1550d70bcc821c22f7001b66f4c7b83842661b8391eYuichiro Hanada                testFilenameSuffixes[i] = "testSwitchingLanguages" + i;
1568aaae56cf6694ec75043be56f1c7812a343b24d5Yuichiro Hanada                final String fileName = UserHistoryPredictionDictionary.NAME + "." +
1578aaae56cf6694ec75043be56f1c7812a343b24d5Yuichiro Hanada                        testFilenameSuffixes[i] + ExpandableBinaryDictionary.DICT_FILE_EXTENSION;
15828a70b63c5748783c3b6fcac551cb69852840474Keisuke Kuroynagi                dictFiles[i] = new File(getContext().getFilesDir(), fileName);
15928a70b63c5748783c3b6fcac551cb69852840474Keisuke Kuroynagi            }
16028a70b63c5748783c3b6fcac551cb69852840474Keisuke Kuroynagi
1610d70bcc821c22f7001b66f4c7b83842661b8391eYuichiro Hanada            final long start = System.currentTimeMillis();
16228a70b63c5748783c3b6fcac551cb69852840474Keisuke Kuroynagi
16328a70b63c5748783c3b6fcac551cb69852840474Keisuke Kuroynagi            for (int i = 0; i < numberOfLanguageSwitching; i++) {
16428a70b63c5748783c3b6fcac551cb69852840474Keisuke Kuroynagi                final int index = i % numberOfLanguages;
1650d70bcc821c22f7001b66f4c7b83842661b8391eYuichiro Hanada                // Switch languages to testFilenameSuffixes[index].
1660d70bcc821c22f7001b66f4c7b83842661b8391eYuichiro Hanada                addAndWriteRandomWords(testFilenameSuffixes[index],
167e5a35711b854aedeeea2f45105b941b9deee49bcSatoshi Kataoka                        numberOfWordsInsertedForEachLanguageSwitch, random,
168e5a35711b854aedeeea2f45105b941b9deee49bcSatoshi Kataoka                        false /* checksContents */);
16928a70b63c5748783c3b6fcac551cb69852840474Keisuke Kuroynagi            }
17028a70b63c5748783c3b6fcac551cb69852840474Keisuke Kuroynagi
17128a70b63c5748783c3b6fcac551cb69852840474Keisuke Kuroynagi            final long end = System.currentTimeMillis();
17228a70b63c5748783c3b6fcac551cb69852840474Keisuke Kuroynagi            Log.d(TAG, "testStressTestForSwitchingLanguageAndAddingWords took "
1730d70bcc821c22f7001b66f4c7b83842661b8391eYuichiro Hanada                    + (end - start) + " ms");
1740d70bcc821c22f7001b66f4c7b83842661b8391eYuichiro Hanada        } finally {
17528a70b63c5748783c3b6fcac551cb69852840474Keisuke Kuroynagi            try {
17628a70b63c5748783c3b6fcac551cb69852840474Keisuke Kuroynagi                Log.d(TAG, "waiting for writing ...");
1770d70bcc821c22f7001b66f4c7b83842661b8391eYuichiro Hanada                Thread.sleep(TimeUnit.MILLISECONDS.convert(5L, TimeUnit.SECONDS));
17828a70b63c5748783c3b6fcac551cb69852840474Keisuke Kuroynagi            } catch (InterruptedException e) {
17928a70b63c5748783c3b6fcac551cb69852840474Keisuke Kuroynagi                Log.d(TAG, "InterruptedException: " + e);
18028a70b63c5748783c3b6fcac551cb69852840474Keisuke Kuroynagi            }
18128a70b63c5748783c3b6fcac551cb69852840474Keisuke Kuroynagi            for (final File file : dictFiles) {
18228a70b63c5748783c3b6fcac551cb69852840474Keisuke Kuroynagi                if (file != null) {
1830d70bcc821c22f7001b66f4c7b83842661b8391eYuichiro Hanada                    assertTrue(file.exists());
1840d70bcc821c22f7001b66f4c7b83842661b8391eYuichiro Hanada                    assertTrue(file.length() >= MIN_USER_HISTORY_DICTIONARY_FILE_SIZE);
18528a70b63c5748783c3b6fcac551cb69852840474Keisuke Kuroynagi                    file.delete();
18628a70b63c5748783c3b6fcac551cb69852840474Keisuke Kuroynagi                }
18728a70b63c5748783c3b6fcac551cb69852840474Keisuke Kuroynagi            }
18828a70b63c5748783c3b6fcac551cb69852840474Keisuke Kuroynagi        }
18928a70b63c5748783c3b6fcac551cb69852840474Keisuke Kuroynagi    }
19084d858ed5e187eb9d4b56b593e1d9287f762bbcaYuichiro Hanada}
191