187380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne/*
287380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne * Copyright (C) 2010 The Android Open Source Project
387380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne *
487380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne * Licensed under the Apache License, Version 2.0 (the "License");
587380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne * you may not use this file except in compliance with the License.
687380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne * You may obtain a copy of the License at
787380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne *
887380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne *      http://www.apache.org/licenses/LICENSE-2.0
987380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne *
1087380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne * Unless required by applicable law or agreed to in writing, software
1187380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne * distributed under the License is distributed on an "AS IS" BASIS,
1287380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1387380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne * See the License for the specific language governing permissions and
1487380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne * limitations under the License.
1587380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne*/
1687380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne
1787380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunnepackage android.widget;
1887380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne
1987380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunneimport android.test.AndroidTestCase;
2087380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunneimport android.test.suitebuilder.annotation.LargeTest;
2187380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunneimport android.text.InputType;
2287380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunneimport android.text.Selection;
2387380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunneimport android.text.Spannable;
2487380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunneimport android.text.SpannableString;
2587380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne
2687380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunneimport java.lang.reflect.Field;
2787380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunneimport java.lang.reflect.InvocationTargetException;
2887380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunneimport java.lang.reflect.Method;
2987380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne
3087380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne/**
3187380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne * TextViewPatchTest tests {@link TextView}'s definition of word. Finds and
3287380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne * verifies word limits to be in strings containing different kinds of
3387380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne * characters.
3487380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne */
3587380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunnepublic class TextViewWordLimitsTest extends AndroidTestCase {
3687380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne
3787380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne    TextView mTv = null;
3887380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne    Method mGetWordLimits, mSelectCurrentWord;
3987380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne    Field mContextMenuTriggeredByKey, mSelectionControllerEnabled;
4087380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne
4187380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne
4287380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne    /**
4387380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne     * Sets up common fields used in all test cases.
4487380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne     * @throws NoSuchFieldException
4587380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne     * @throws SecurityException
4687380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne     */
4787380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne    @Override
4887380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne    protected void setUp() throws NoSuchMethodException, SecurityException, NoSuchFieldException {
4987380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne        mTv = new TextView(getContext());
5087380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne        mTv.setInputType(InputType.TYPE_CLASS_TEXT);
5187380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne
5287380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne        mGetWordLimits = mTv.getClass().getDeclaredMethod("getWordLimitsAt",
5387380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne                new Class[] {int.class});
5487380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne        mGetWordLimits.setAccessible(true);
5587380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne
5687380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne        mSelectCurrentWord = mTv.getClass().getDeclaredMethod("selectCurrentWord", new Class[] {});
5787380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne        mSelectCurrentWord.setAccessible(true);
5887380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne
5987380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne        mContextMenuTriggeredByKey = mTv.getClass().getDeclaredField("mContextMenuTriggeredByKey");
6087380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne        mContextMenuTriggeredByKey.setAccessible(true);
6187380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne        mSelectionControllerEnabled = mTv.getClass().getDeclaredField("mSelectionControllerEnabled");
6287380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne        mSelectionControllerEnabled.setAccessible(true);
6387380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne    }
6487380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne
6587380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne    /**
6687380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne     * Calculate and verify word limits. Depends on the TextView implementation.
6787380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne     * Uses a private method and internal data representation.
6887380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne     *
6987380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne     * @param text         Text to select a word from
7087380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne     * @param pos          Position to expand word around
7187380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne     * @param correctStart Correct start position for the word
7287380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne     * @param correctEnd   Correct end position for the word
7387380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne     * @throws InvocationTargetException
7487380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne     * @throws IllegalAccessException
7587380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne     * @throws IllegalArgumentException
7687380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne     * @throws InvocationTargetException
7787380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne     * @throws IllegalAccessException
7887380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne     */
7987380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne    private void verifyWordLimits(String text, int pos, int correctStart, int correctEnd)
8087380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne    throws IllegalArgumentException, IllegalAccessException, InvocationTargetException {
8187380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne        mTv.setText(text, TextView.BufferType.SPANNABLE);
8287380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne
8387380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne        long limits = (Long)mGetWordLimits.invoke(mTv, new Object[] {new Integer(pos)});
8487380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne        int actualStart = (int)(limits >>> 32);
8587380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne        int actualEnd = (int)(limits & 0x00000000FFFFFFFFL);
8687380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne        assertEquals(correctStart, actualStart);
8787380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne        assertEquals(correctEnd, actualEnd);
8887380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne    }
8987380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne
9087380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne
9187380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne    private void verifySelectCurrentWord(Spannable text, int selectionStart, int selectionEnd, int correctStart,
9287380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne            int correctEnd) throws InvocationTargetException, IllegalAccessException {
9387380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne        mTv.setText(text, TextView.BufferType.SPANNABLE);
9487380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne
9587380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne        Selection.setSelection((Spannable)mTv.getText(), selectionStart, selectionEnd);
9687380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne        mContextMenuTriggeredByKey.setBoolean(mTv, true);
9787380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne        mSelectionControllerEnabled.setBoolean(mTv, true);
9887380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne        mSelectCurrentWord.invoke(mTv);
9987380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne
10087380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne        assertEquals(correctStart, mTv.getSelectionStart());
10187380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne        assertEquals(correctEnd, mTv.getSelectionEnd());
10287380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne    }
10387380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne
10487380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne
10587380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne    /**
10687380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne     * Corner cases for string length.
10787380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne     */
10887380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne    @LargeTest
10987380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne    public void testLengths() throws Exception {
11087380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne        final String ONE_TWO = "one two";
11187380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne        final String EMPTY   = "";
11287380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne        final String TOOLONG = "ThisWordIsTooLongToBeDefinedAsAWordInTheSenseUsedInTextView";
11387380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne
11487380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne        // Select first word
11587380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne        verifyWordLimits(ONE_TWO, 0, 0, 3);
11687380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne        verifyWordLimits(ONE_TWO, 3, 0, 3);
11787380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne
11887380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne        // Select last word
11987380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne        verifyWordLimits(ONE_TWO, 4, 4, 7);
12087380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne        verifyWordLimits(ONE_TWO, 7, 4, 7);
12187380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne
12287380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne        // Empty string
12387380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne        verifyWordLimits(EMPTY, 0, -1, -1);
12487380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne
12587380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne        // Too long word
12687380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne        verifyWordLimits(TOOLONG, 0, -1, -1);
12787380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne    }
12887380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne
12987380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne    /**
13087380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne     * Unicode classes included.
13187380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne     */
13287380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne    @LargeTest
13387380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne    public void testIncludedClasses() throws Exception {
13487380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne        final String LOWER          = "njlj";
13587380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne        final String UPPER          = "NJLJ";
13687380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne        final String TITLECASE      = "\u01C8\u01CB\u01F2"; // Lj Nj Dz
13787380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne        final String OTHER          = "\u3042\u3044\u3046"; // Hiragana AIU
13887380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne        final String MODIFIER       = "\u02C6\u02CA\u02CB"; // Circumflex Acute Grave
13987380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne
14087380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne        // Each string contains a single valid word
14187380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne        verifyWordLimits(LOWER, 1, 0, 4);
14287380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne        verifyWordLimits(UPPER, 1, 0, 4);
14387380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne        verifyWordLimits(TITLECASE, 1, 0, 3);
14487380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne        verifyWordLimits(OTHER, 1, 0, 3);
14587380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne        verifyWordLimits(MODIFIER, 1, 0, 3);
14687380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne    }
14787380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne
14887380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne    /**
14987380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne     * Unicode classes included if combined with a letter.
15087380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne     */
15187380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne    @LargeTest
15287380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne    public void testPartlyIncluded() throws Exception {
15387380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne        final String NUMBER           = "123";
15487380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne        final String NUMBER_LOWER     = "1st";
15587380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne        final String APOSTROPHE       = "''";
15687380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne        final String APOSTROPHE_LOWER = "'Android's'";
15787380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne
15887380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne        // Pure decimal number is ignored
15987380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne        verifyWordLimits(NUMBER, 1, -1, -1);
16087380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne
16187380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne        // Number with letter is valid
16287380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne        verifyWordLimits(NUMBER_LOWER, 1, 0, 3);
16387380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne
16487380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne        // Stand apostrophes are ignore
16587380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne        verifyWordLimits(APOSTROPHE, 1, -1, -1);
16687380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne
16787380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne        // Apostrophes are accepted if they are a part of a word
16887380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne        verifyWordLimits(APOSTROPHE_LOWER, 1, 0, 11);
16987380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne    }
17087380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne
17187380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne    /**
17287380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne     * Unicode classes included if combined with a letter.
17387380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne     */
17487380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne    @LargeTest
17587380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne    public void testFinalSeparator() throws Exception {
17687380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne        final String PUNCTUATION = "abc, def.";
17787380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne
17887380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne        // Starting from the comma
17987380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne        verifyWordLimits(PUNCTUATION, 3, 0, 3);
18087380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne        verifyWordLimits(PUNCTUATION, 4, 0, 4);
18187380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne
18287380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne        // Starting from the final period
18387380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne        verifyWordLimits(PUNCTUATION, 8, 5, 8);
18487380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne        verifyWordLimits(PUNCTUATION, 9, 5, 9);
18587380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne    }
18687380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne
18787380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne    /**
18887380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne     * Unicode classes other than listed in testIncludedClasses and
18987380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne     * testPartlyIncluded act as word separators.
19087380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne     */
19187380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne    @LargeTest
19287380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne    public void testNotIncluded() throws Exception {
19387380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne        // Selection of character classes excluded
19487380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne        final String MARK_NONSPACING        = "a\u030A";       // a Combining ring above
19587380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne        final String PUNCTUATION_OPEN_CLOSE = "(c)";           // Parenthesis
19687380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne        final String PUNCTUATION_DASH       = "non-fiction";   // Hyphen
19787380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne        final String PUNCTUATION_OTHER      = "b&b";           // Ampersand
19887380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne        final String SYMBOL_OTHER           = "Android\u00AE"; // Registered
19987380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne        final String SEPARATOR_SPACE        = "one two";       // Space
20087380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne
20187380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne        // "a"
20287380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne        verifyWordLimits(MARK_NONSPACING, 1, 0, 1);
20387380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne
20487380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne        // "c"
20587380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne        verifyWordLimits(PUNCTUATION_OPEN_CLOSE, 1, 1, 2);
20687380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne
20787380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne        // "non-"
20887380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne        verifyWordLimits(PUNCTUATION_DASH, 3, 0, 3);
20987380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne        verifyWordLimits(PUNCTUATION_DASH, 4, 4, 11);
21087380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne
21187380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne        // "b"
21287380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne        verifyWordLimits(PUNCTUATION_OTHER, 0, 0, 1);
21387380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne        verifyWordLimits(PUNCTUATION_OTHER, 1, 0, 1);
21487380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne        verifyWordLimits(PUNCTUATION_OTHER, 2, 0, 3); // & is considered a punctuation sign.
21587380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne        verifyWordLimits(PUNCTUATION_OTHER, 3, 2, 3);
21687380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne
21787380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne        // "Android"
21887380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne        verifyWordLimits(SYMBOL_OTHER, 7, 0, 7);
21987380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne        verifyWordLimits(SYMBOL_OTHER, 8, -1, -1);
22087380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne
22187380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne        // "one"
22287380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne        verifyWordLimits(SEPARATOR_SPACE, 1, 0, 3);
22387380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne    }
22487380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne
22587380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne    /**
22687380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne     * Surrogate characters are treated as their code points.
22787380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne     */
22887380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne    @LargeTest
22987380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne    public void testSurrogate() throws Exception {
23087380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne        final String SURROGATE_LETTER   = "\uD800\uDC00\uD800\uDC01\uD800\uDC02"; // Linear B AEI
23187380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne        final String SURROGATE_SYMBOL   = "\uD83D\uDE01\uD83D\uDE02\uD83D\uDE03"; // Three smileys
23287380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne
23387380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne        // Letter Other is included even when coded as surrogate pairs
23479ff914f2183a5f0a4121ef06112409e1c699ae7Gilles Debunne        verifyWordLimits(SURROGATE_LETTER, 0, 0, 6);
23579ff914f2183a5f0a4121ef06112409e1c699ae7Gilles Debunne        verifyWordLimits(SURROGATE_LETTER, 1, 0, 6);
23679ff914f2183a5f0a4121ef06112409e1c699ae7Gilles Debunne        verifyWordLimits(SURROGATE_LETTER, 2, 0, 6);
23779ff914f2183a5f0a4121ef06112409e1c699ae7Gilles Debunne        verifyWordLimits(SURROGATE_LETTER, 3, 0, 6);
23879ff914f2183a5f0a4121ef06112409e1c699ae7Gilles Debunne        verifyWordLimits(SURROGATE_LETTER, 4, 0, 6);
23979ff914f2183a5f0a4121ef06112409e1c699ae7Gilles Debunne        verifyWordLimits(SURROGATE_LETTER, 5, 0, 6);
24079ff914f2183a5f0a4121ef06112409e1c699ae7Gilles Debunne        verifyWordLimits(SURROGATE_LETTER, 6, 0, 6);
24187380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne
24287380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne        // Not included classes are ignored even when coded as surrogate pairs
24379ff914f2183a5f0a4121ef06112409e1c699ae7Gilles Debunne        verifyWordLimits(SURROGATE_SYMBOL, 0, -1, -1);
24487380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne        verifyWordLimits(SURROGATE_SYMBOL, 1, -1, -1);
24587380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne        verifyWordLimits(SURROGATE_SYMBOL, 2, -1, -1);
24679ff914f2183a5f0a4121ef06112409e1c699ae7Gilles Debunne        verifyWordLimits(SURROGATE_SYMBOL, 3, -1, -1);
24779ff914f2183a5f0a4121ef06112409e1c699ae7Gilles Debunne        verifyWordLimits(SURROGATE_SYMBOL, 4, -1, -1);
24879ff914f2183a5f0a4121ef06112409e1c699ae7Gilles Debunne        verifyWordLimits(SURROGATE_SYMBOL, 5, -1, -1);
24979ff914f2183a5f0a4121ef06112409e1c699ae7Gilles Debunne        verifyWordLimits(SURROGATE_SYMBOL, 6, -1, -1);
25087380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne    }
25187380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne
25287380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne    /**
25387380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne     * Selection is used if present and valid word.
25487380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne     */
25587380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne    @LargeTest
25687380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne    public void testSelectCurrentWord() throws Exception {
25787380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne        SpannableString textLower       = new SpannableString("first second");
25887380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne        SpannableString textOther       = new SpannableString("\u3042\3044\3046"); // Hiragana AIU
25987380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne        SpannableString textDash        = new SpannableString("non-fiction");      // Hyphen
26087380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne        SpannableString textPunctOther  = new SpannableString("b&b");              // Ampersand
26187380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne        SpannableString textSymbolOther = new SpannableString("Android\u00AE");    // Registered
26287380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne
26387380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne        // Valid selection - Letter, Lower
26487380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne        verifySelectCurrentWord(textLower, 2, 5, 0, 5);
26587380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne
26687380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne        // Adding the space spreads to the second word
26787380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne        verifySelectCurrentWord(textLower, 2, 6, 0, 12);
26887380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne
26987380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne        // Valid selection -- Letter, Other
27087380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne        verifySelectCurrentWord(textOther, 1, 2, 0, 5);
27187380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne
27287380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne        // Zero-width selection is interpreted as a cursor and the selection is ignored
27387380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne        verifySelectCurrentWord(textLower, 2, 2, 0, 5);
27487380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne
27587380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne        // Hyphen is part of selection
27687380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne        verifySelectCurrentWord(textDash, 2, 5, 0, 11);
27787380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne
27887380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne        // Ampersand part of selection or not
27987380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne        verifySelectCurrentWord(textPunctOther, 1, 2, 0, 3);
28087380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne        verifySelectCurrentWord(textPunctOther, 1, 3, 0, 3);
28187380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne
28287380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne        // (R) part of the selection
28387380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne        verifySelectCurrentWord(textSymbolOther, 2, 7, 0, 7);
28487380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne        verifySelectCurrentWord(textSymbolOther, 2, 8, 0, 8);
28587380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne    }
28687380bcaebe63bdcd44828f137b2b2b0ba952f0aGilles Debunne}
287