xref: /aosp_15_r20/cts/tests/tests/text/src/android/text/style/cts/SuggestionSpanTest.java (revision b7c941bb3fa97aba169d73cee0bed2de8ac964bf)
1 /*
2  * Copyright (C) 2016 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 android.text.style.cts;
18 
19 import static org.junit.Assert.assertArrayEquals;
20 import static org.junit.Assert.assertEquals;
21 import static org.junit.Assert.assertFalse;
22 import static org.junit.Assert.assertTrue;
23 
24 import android.content.Context;
25 import android.content.res.Configuration;
26 import android.graphics.Color;
27 import android.os.LocaleList;
28 import android.os.Parcel;
29 import android.text.SpannableString;
30 import android.text.Spanned;
31 import android.text.StaticLayout;
32 import android.text.TextPaint;
33 import android.text.style.SuggestionSpan;
34 
35 import androidx.annotation.NonNull;
36 import androidx.annotation.Nullable;
37 import androidx.test.ext.junit.runners.AndroidJUnit4;
38 import androidx.test.filters.SmallTest;
39 import androidx.test.platform.app.InstrumentationRegistry;
40 
41 import org.junit.Test;
42 import org.junit.runner.RunWith;
43 
44 import java.util.Locale;
45 
46 /**
47  * Test {@link SuggestionSpan}.
48  */
49 @SmallTest
50 @RunWith(AndroidJUnit4.class)
51 public class SuggestionSpanTest {
52     @Test
testConstructorWithContext()53     public void testConstructorWithContext() {
54         final String[] suggestions = new String[] {"suggestion1", "suggestion2"};
55         final Configuration overrideConfig = new Configuration();
56         final Locale locale = Locale.forLanguageTag("az-Arab");
57         overrideConfig.setLocales(new LocaleList(locale));
58         final Context context = InstrumentationRegistry.getInstrumentation().getTargetContext()
59                         .createConfigurationContext(overrideConfig);
60 
61         final SuggestionSpan span = new SuggestionSpan(context, suggestions,
62                 SuggestionSpan.FLAG_AUTO_CORRECTION);
63 
64         assertEquals(locale, span.getLocaleObject());
65         assertArrayEquals(suggestions, span.getSuggestions());
66         assertEquals(SuggestionSpan.FLAG_AUTO_CORRECTION, span.getFlags());
67     }
68 
69     @Test
testGetSuggestionSpans()70     public void testGetSuggestionSpans() {
71         final String[] suggestions = new String[]{"suggestion1", "suggestion2"};
72         final SuggestionSpan span = new SuggestionSpan(Locale.forLanguageTag("en"), suggestions,
73                 SuggestionSpan.FLAG_AUTO_CORRECTION);
74         assertArrayEquals("Should return the correct suggestions array",
75                 suggestions, span.getSuggestions());
76 
77         final SuggestionSpan clonedSpan = cloneViaParcel(span);
78         assertArrayEquals("Should (de)serialize suggestions",
79                 suggestions, clonedSpan.getSuggestions());
80     }
81 
82     @Test
testGetSuggestionSpans_emptySuggestions()83     public void testGetSuggestionSpans_emptySuggestions() {
84         final String[] suggestions = new String[0];
85         final SuggestionSpan span = new SuggestionSpan(Locale.forLanguageTag("en"), suggestions,
86                 SuggestionSpan.FLAG_AUTO_CORRECTION);
87         assertArrayEquals("Span should return empty suggestion array",
88                 suggestions, span.getSuggestions());
89 
90         // also test parceling
91         final SuggestionSpan clonedSpan = cloneViaParcel(span);
92         assertArrayEquals("Should (de)serialize empty suggestions array",
93                 suggestions, clonedSpan.getSuggestions());
94     }
95 
96     @Test
testGetSuggestionSpans_suggestionsWithNullValue()97     public void testGetSuggestionSpans_suggestionsWithNullValue() {
98         final String[] suggestions = new String[]{"suggestion", null};
99         final SuggestionSpan span = new SuggestionSpan(Locale.forLanguageTag("en"), suggestions,
100                 SuggestionSpan.FLAG_AUTO_CORRECTION);
101         assertArrayEquals("Should accept and return null suggestions",
102                 suggestions, span.getSuggestions());
103 
104         final SuggestionSpan clonedSpan = cloneViaParcel(span);
105         assertArrayEquals("Should (de)serialize null in suggestions array",
106                 suggestions, clonedSpan.getSuggestions());
107     }
108 
109     @Test
testGetFlags()110     public void testGetFlags() {
111         final String[] anySuggestions = new String[0];
112         final int flag = SuggestionSpan.FLAG_AUTO_CORRECTION;
113         SuggestionSpan span = new SuggestionSpan(Locale.forLanguageTag("en"), anySuggestions, flag);
114 
115         assertEquals("Should return the flag passed in constructor",
116                 flag, span.getFlags());
117 
118         final SuggestionSpan clonedSpan = cloneViaParcel(span);
119         assertEquals("Should (de)serialize flags", flag, clonedSpan.getFlags());
120     }
121 
122     @Test
testEquals_returnsTrueForDeserializedInstances()123     public void testEquals_returnsTrueForDeserializedInstances() {
124         final SuggestionSpan span1 = new SuggestionSpan(null, Locale.forLanguageTag("en"),
125                 new String[0], SuggestionSpan.FLAG_AUTO_CORRECTION, SuggestionSpan.class);
126         final SuggestionSpan span2 = cloneViaParcel(span1);
127 
128         assertTrue("(De)serialized instances should be equal", span1.equals(span2));
129     }
130 
131     @Test
testEquals_returnsTrueIfTheFlagsAreDifferent()132     public void testEquals_returnsTrueIfTheFlagsAreDifferent() {
133         final SuggestionSpan span1 = new SuggestionSpan(null, Locale.forLanguageTag("en"),
134                 new String[0], SuggestionSpan.FLAG_AUTO_CORRECTION, SuggestionSpan.class);
135         final SuggestionSpan span2 = cloneViaParcel(span1);
136         span2.setFlags(SuggestionSpan.FLAG_EASY_CORRECT);
137 
138         assertEquals("Should return the flag passed in set function",
139                 SuggestionSpan.FLAG_EASY_CORRECT, span2.getFlags());
140 
141         assertTrue("Instances with different flags should be equal", span1.equals(span2));
142     }
143 
144     @Test
testEquals_returnsFalseIfCreationTimeIsNotSame()145     public void testEquals_returnsFalseIfCreationTimeIsNotSame() {
146         final Locale anyLocale = Locale.forLanguageTag("en");
147         final String[] anySuggestions = new String[0];
148         final int anyFlags = SuggestionSpan.FLAG_AUTO_CORRECTION;
149         final Class anyClass = SuggestionSpan.class;
150 
151         final SuggestionSpan span1 = new SuggestionSpan(null, anyLocale, anySuggestions, anyFlags,
152                 anyClass);
153         try {
154             // let some time pass before constructing the other span
155             Thread.sleep(2);
156         } catch (InterruptedException e) {
157             // ignore
158         }
159         final SuggestionSpan span2 = new SuggestionSpan(null, anyLocale, anySuggestions, anyFlags,
160                 anyClass);
161 
162         assertFalse("Instances created at different time should not be equal", span2.equals(span1));
163     }
164 
165     /**
166      * @param locale a {@link Locale} object.
167      * @return A well-formed BCP 47 language tag representation.
168      */
169     @Nullable
toWellFormedLocale(@ullable final Locale locale)170     private Locale toWellFormedLocale(@Nullable final Locale locale) {
171         if (locale == null) {
172             return null;
173         }
174         // Drop all the malformed data.
175         return Locale.forLanguageTag(locale.toLanguageTag());
176     }
177 
178     @NonNull
getNonNullLocaleString(@ullable final Locale original)179     private String getNonNullLocaleString(@Nullable final Locale original) {
180         if (original == null) {
181             return "";
182         }
183         return original.toString();
184     }
185 
verifyGetLocaleObject(final Locale locale)186     private void verifyGetLocaleObject(final Locale locale) {
187         final SuggestionSpan span = new SuggestionSpan(locale, new String[0],
188                 SuggestionSpan.FLAG_AUTO_CORRECTION);
189         // In the context of SuggestionSpan#getLocaleObject(), we do care only about subtags that
190         // can be interpreted as LanguageTag.
191         assertEquals(toWellFormedLocale(locale), span.getLocaleObject());
192         assertEquals(getNonNullLocaleString(locale), span.getLocale());
193 
194         final SuggestionSpan cloned = cloneViaParcel(span);
195         assertEquals(span, cloned);
196         assertEquals(toWellFormedLocale(locale), cloned.getLocaleObject());
197         assertEquals(getNonNullLocaleString(locale), cloned.getLocale());
198     }
199 
200     @Test
testGetLocaleObject()201     public void testGetLocaleObject() {
202         verifyGetLocaleObject(Locale.forLanguageTag("en"));
203         verifyGetLocaleObject(Locale.forLanguageTag("en-GB"));
204         verifyGetLocaleObject(Locale.forLanguageTag("EN-GB"));
205         verifyGetLocaleObject(Locale.forLanguageTag("en-gb"));
206         verifyGetLocaleObject(Locale.forLanguageTag("En-gB"));
207         verifyGetLocaleObject(Locale.forLanguageTag("und"));
208         verifyGetLocaleObject(Locale.forLanguageTag("de-DE-u-co-phonebk"));
209         verifyGetLocaleObject(Locale.forLanguageTag(""));
210         verifyGetLocaleObject(null);
211         verifyGetLocaleObject(new Locale(" an  ", " i n v a l i d ", "data"));
212     }
213 
214     // Measures the width of some potentially-spanned text, assuming it's not too wide.
textWidth(CharSequence text)215     private float textWidth(CharSequence text) {
216         final TextPaint tp = new TextPaint();
217         tp.setTextSize(100.0f); // Large enough so that the difference in kerning is visible.
218         final int largeWidth = 10000; // Enough width so the whole text fits in one line.
219         final StaticLayout layout = StaticLayout.Builder.obtain(
220                 text, 0, text.length(), tp, largeWidth).build();
221         return layout.getLineWidth(0);
222     }
223 
224     @Test
testDoesntAffectWidth()225     public void testDoesntAffectWidth() {
226         // Roboto kerns between "P" and "."
227         final SpannableString text = new SpannableString("P.");
228         final float origLineWidth = textWidth(text);
229 
230         final String[] suggestions = new String[]{"suggestion1", "suggestion2"};
231         final SuggestionSpan span = new SuggestionSpan(Locale.US, suggestions,
232                 SuggestionSpan.FLAG_AUTO_CORRECTION);
233         // Put just the "P" in a suggestion span.
234         text.setSpan(span, 0, 1, Spanned.SPAN_INCLUSIVE_INCLUSIVE);
235         final float underlinedLineWidth = textWidth(text);
236         assertEquals(origLineWidth, underlinedLineWidth, 0.0f);
237     }
238 
239     @NonNull
cloneViaParcel(@onNull final SuggestionSpan original)240     SuggestionSpan cloneViaParcel(@NonNull final SuggestionSpan original) {
241         Parcel parcel = null;
242         try {
243             parcel = Parcel.obtain();
244             original.writeToParcel(parcel, 0);
245             parcel.setDataPosition(0);
246             return new SuggestionSpan(parcel);
247         } finally {
248             if (parcel != null) {
249                 parcel.recycle();
250             }
251         }
252     }
253 
254     @Test
testGetUnderlineColor_NoUnderline()255     public void testGetUnderlineColor_NoUnderline() {
256         final String[] suggestions = new String[0];
257         final SuggestionSpan span = new SuggestionSpan(Locale.forLanguageTag("en"), suggestions, 0);
258         assertEquals(span.getUnderlineColor(), 0);
259     }
260 
261     @Test
testGetUnderlineColor_EasyCorrectUnderline()262     public void testGetUnderlineColor_EasyCorrectUnderline() {
263         final String[] suggestions = new String[0];
264         final SuggestionSpan span = new SuggestionSpan(Locale.forLanguageTag("en"), suggestions,
265                 SuggestionSpan.FLAG_EASY_CORRECT);
266         assertEquals(span.getUnderlineColor(), Color.BLACK);
267     }
268 
269     @Test
testGetUnderlineColor_MisspelledUnderline()270     public void testGetUnderlineColor_MisspelledUnderline() {
271         final String[] suggestions = new String[0];
272         final SuggestionSpan span = new SuggestionSpan(Locale.forLanguageTag("en"), suggestions,
273                 SuggestionSpan.FLAG_EASY_CORRECT | SuggestionSpan.FLAG_MISSPELLED);
274         assertEquals(span.getUnderlineColor(), Color.BLACK);
275     }
276 
277     @Test
testGetUnderlineColor_AutoCorrectionUnderline()278     public void testGetUnderlineColor_AutoCorrectionUnderline() {
279         final String[] suggestions = new String[0];
280         final SuggestionSpan span = new SuggestionSpan(Locale.forLanguageTag("en"), suggestions,
281                 SuggestionSpan.FLAG_AUTO_CORRECTION);
282         assertEquals(span.getUnderlineColor(), Color.BLACK);
283     }
284 }
285