1 /*
2  * Copyright (C) 2013 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.utils;
18 
19 import static org.junit.Assert.assertEquals;
20 import static org.junit.Assert.assertTrue;
21 
22 import android.content.Context;
23 import android.text.SpannableString;
24 import android.text.SpannableStringBuilder;
25 import android.text.Spanned;
26 import android.text.SpannedString;
27 import android.text.style.SuggestionSpan;
28 import android.text.style.URLSpan;
29 
30 import androidx.test.InstrumentationRegistry;
31 import androidx.test.filters.SmallTest;
32 import androidx.test.runner.AndroidJUnit4;
33 
34 import org.junit.Test;
35 import org.junit.runner.RunWith;
36 
37 @SmallTest
38 @RunWith(AndroidJUnit4.class)
39 public class SpannableStringUtilsTests {
40 
getContext()41     private Context getContext() {
42         return InstrumentationRegistry.getTargetContext();
43     }
44 
45     @Test
testConcatWithSuggestionSpansOnly()46     public void testConcatWithSuggestionSpansOnly() {
47         SpannableStringBuilder s = new SpannableStringBuilder("test string\ntest string\n"
48                 + "test string\ntest string\ntest string\ntest string\ntest string\ntest string\n"
49                 + "test string\ntest string\n");
50         final int N = 10;
51         for (int i = 0; i < N; ++i) {
52             // Put a PARAGRAPH-flagged span that should not be found in the result.
53             s.setSpan(new SuggestionSpan(getContext(),
54                     new String[] {"" + i}, Spanned.SPAN_PARAGRAPH),
55                     i * 12, i * 12 + 12, Spanned.SPAN_PARAGRAPH);
56             // Put a normal suggestion span that should be found in the result.
57             s.setSpan(new SuggestionSpan(getContext(), new String[] {"" + i}, 0), i, i * 2, 0);
58             // Put a URL span than should not be found in the result.
59             s.setSpan(new URLSpan("http://a"), i, i * 2, 0);
60         }
61 
62         final CharSequence a = s.subSequence(0, 15);
63         final CharSequence b = s.subSequence(15, s.length());
64         final Spanned result =
65                 (Spanned)SpannableStringUtils.concatWithNonParagraphSuggestionSpansOnly(a, b);
66 
67         Object[] spans = result.getSpans(0, result.length(), SuggestionSpan.class);
68         for (int i = 0; i < spans.length; i++) {
69             final int flags = result.getSpanFlags(spans[i]);
70             assertEquals("Should not find a span with PARAGRAPH flag",
71                     flags & Spanned.SPAN_PARAGRAPH, 0);
72             assertTrue("Should be a SuggestionSpan", spans[i] instanceof SuggestionSpan);
73         }
74     }
75 
assertSpanCount(final int expectedCount, final CharSequence cs)76     private static void assertSpanCount(final int expectedCount, final CharSequence cs) {
77         final int actualCount;
78         if (cs instanceof Spanned) {
79             final Spanned spanned = (Spanned) cs;
80             actualCount = spanned.getSpans(0, spanned.length(), Object.class).length;
81         } else {
82             actualCount = 0;
83         }
84         assertEquals(expectedCount, actualCount);
85     }
86 
assertSpan(final CharSequence cs, final Object expectedSpan, final int expectedStart, final int expectedEnd, final int expectedFlags)87     private static void assertSpan(final CharSequence cs, final Object expectedSpan,
88             final int expectedStart, final int expectedEnd, final int expectedFlags) {
89         assertTrue(cs instanceof Spanned);
90         final Spanned spanned = (Spanned) cs;
91         final Object[] actualSpans = spanned.getSpans(0, spanned.length(), Object.class);
92         for (Object actualSpan : actualSpans) {
93             if (actualSpan == expectedSpan) {
94                 final int actualStart = spanned.getSpanStart(actualSpan);
95                 final int actualEnd = spanned.getSpanEnd(actualSpan);
96                 final int actualFlags = spanned.getSpanFlags(actualSpan);
97                 assertEquals(expectedStart, actualStart);
98                 assertEquals(expectedEnd, actualEnd);
99                 assertEquals(expectedFlags, actualFlags);
100                 return;
101             }
102         }
103         assertTrue(false);
104     }
105 
106     @Test
testSplitCharSequenceWithSpan()107     public void testSplitCharSequenceWithSpan() {
108         // text:  " a bcd efg hij  "
109         // span1:  ^^^^^^^
110         // span2:  ^^^^^
111         // span3:              ^
112         final SpannableString spannableString = new SpannableString(" a bcd efg hij  ");
113         final Object span1 = new Object();
114         final Object span2 = new Object();
115         final Object span3 = new Object();
116         final int SPAN1_FLAGS = Spanned.SPAN_EXCLUSIVE_EXCLUSIVE;
117         final int SPAN2_FLAGS = Spanned.SPAN_EXCLUSIVE_INCLUSIVE;
118         final int SPAN3_FLAGS = Spanned.SPAN_INCLUSIVE_INCLUSIVE;
119         spannableString.setSpan(span1, 0, 7, SPAN1_FLAGS);
120         spannableString.setSpan(span2, 0, 5, SPAN2_FLAGS);
121         spannableString.setSpan(span3, 12, 13, SPAN3_FLAGS);
122         final CharSequence[] charSequencesFromSpanned = SpannableStringUtils.split(
123                 spannableString, " ", true /* preserveTrailingEmptySegmengs */);
124         final CharSequence[] charSequencesFromString = SpannableStringUtils.split(
125                 spannableString.toString(), " ", true /* preserveTrailingEmptySegmengs */);
126 
127 
128         assertEquals(7, charSequencesFromString.length);
129         assertEquals(7, charSequencesFromSpanned.length);
130 
131         // text:  ""
132         // span1: ^
133         // span2: ^
134         // span3:
135         assertEquals("", charSequencesFromString[0].toString());
136         assertSpanCount(0, charSequencesFromString[0]);
137         assertEquals("", charSequencesFromSpanned[0].toString());
138         assertSpanCount(2, charSequencesFromSpanned[0]);
139         assertSpan(charSequencesFromSpanned[0], span1, 0, 0, SPAN1_FLAGS);
140         assertSpan(charSequencesFromSpanned[0], span2, 0, 0, SPAN2_FLAGS);
141 
142         // text:  "a"
143         // span1:  ^
144         // span2:  ^
145         // span3:
146         assertEquals("a", charSequencesFromString[1].toString());
147         assertSpanCount(0, charSequencesFromString[1]);
148         assertEquals("a", charSequencesFromSpanned[1].toString());
149         assertSpanCount(2, charSequencesFromSpanned[1]);
150         assertSpan(charSequencesFromSpanned[1], span1, 0, 1, SPAN1_FLAGS);
151         assertSpan(charSequencesFromSpanned[1], span2, 0, 1, SPAN2_FLAGS);
152 
153         // text:  "bcd"
154         // span1:  ^^^
155         // span2:  ^^
156         // span3:
157         assertEquals("bcd", charSequencesFromString[2].toString());
158         assertSpanCount(0, charSequencesFromString[2]);
159         assertEquals("bcd", charSequencesFromSpanned[2].toString());
160         assertSpanCount(2, charSequencesFromSpanned[2]);
161         assertSpan(charSequencesFromSpanned[2], span1, 0, 3, SPAN1_FLAGS);
162         assertSpan(charSequencesFromSpanned[2], span2, 0, 2, SPAN2_FLAGS);
163 
164         // text:  "efg"
165         // span1:
166         // span2:
167         // span3:
168         assertEquals("efg", charSequencesFromString[3].toString());
169         assertSpanCount(0, charSequencesFromString[3]);
170         assertEquals("efg", charSequencesFromSpanned[3].toString());
171         assertSpanCount(0, charSequencesFromSpanned[3]);
172 
173         // text:  "hij"
174         // span1:
175         // span2:
176         // span3:   ^
177         assertEquals("hij", charSequencesFromString[4].toString());
178         assertSpanCount(0, charSequencesFromString[4]);
179         assertEquals("hij", charSequencesFromSpanned[4].toString());
180         assertSpanCount(1, charSequencesFromSpanned[4]);
181         assertSpan(charSequencesFromSpanned[4], span3, 1, 2, SPAN3_FLAGS);
182 
183         // text:  ""
184         // span1:
185         // span2:
186         // span3:
187         assertEquals("", charSequencesFromString[5].toString());
188         assertSpanCount(0, charSequencesFromString[5]);
189         assertEquals("", charSequencesFromSpanned[5].toString());
190         assertSpanCount(0, charSequencesFromSpanned[5]);
191 
192         // text:  ""
193         // span1:
194         // span2:
195         // span3:
196         assertEquals("", charSequencesFromString[6].toString());
197         assertSpanCount(0, charSequencesFromString[6]);
198         assertEquals("", charSequencesFromSpanned[6].toString());
199         assertSpanCount(0, charSequencesFromSpanned[6]);
200     }
201 
202     @Test
testSplitCharSequencePreserveTrailingEmptySegmengs()203     public void testSplitCharSequencePreserveTrailingEmptySegmengs() {
204         assertEquals(1, SpannableStringUtils.split("", " ",
205                 false /* preserveTrailingEmptySegmengs */).length);
206         assertEquals(1, SpannableStringUtils.split(new SpannedString(""), " ",
207                 false /* preserveTrailingEmptySegmengs */).length);
208 
209         assertEquals(1, SpannableStringUtils.split("", " ",
210                 true /* preserveTrailingEmptySegmengs */).length);
211         assertEquals(1, SpannableStringUtils.split(new SpannedString(""), " ",
212                 true /* preserveTrailingEmptySegmengs */).length);
213 
214         assertEquals(0, SpannableStringUtils.split(" ", " ",
215                 false /* preserveTrailingEmptySegmengs */).length);
216         assertEquals(0, SpannableStringUtils.split(new SpannedString(" "), " ",
217                 false /* preserveTrailingEmptySegmengs */).length);
218 
219         assertEquals(2, SpannableStringUtils.split(" ", " ",
220                 true /* preserveTrailingEmptySegmengs */).length);
221         assertEquals(2, SpannableStringUtils.split(new SpannedString(" "), " ",
222                 true /* preserveTrailingEmptySegmengs */).length);
223 
224         assertEquals(3, SpannableStringUtils.split("a b c  ", " ",
225                 false /* preserveTrailingEmptySegmengs */).length);
226         assertEquals(3, SpannableStringUtils.split(new SpannedString("a b c  "), " ",
227                 false /* preserveTrailingEmptySegmengs */).length);
228 
229         assertEquals(5, SpannableStringUtils.split("a b c  ", " ",
230                 true /* preserveTrailingEmptySegmengs */).length);
231         assertEquals(5, SpannableStringUtils.split(new SpannedString("a b c  "), " ",
232                 true /* preserveTrailingEmptySegmengs */).length);
233 
234         assertEquals(6, SpannableStringUtils.split("a     b ", " ",
235                 false /* preserveTrailingEmptySegmengs */).length);
236         assertEquals(6, SpannableStringUtils.split(new SpannedString("a     b "), " ",
237                 false /* preserveTrailingEmptySegmengs */).length);
238 
239         assertEquals(7, SpannableStringUtils.split("a     b ", " ",
240                 true /* preserveTrailingEmptySegmengs */).length);
241         assertEquals(7, SpannableStringUtils.split(new SpannedString("a     b "), " ",
242                 true /* preserveTrailingEmptySegmengs */).length);
243     }
244 }
245