1 /*
2  * Copyright (C) 2014 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.compat;
18 
19 import static org.junit.Assert.assertEquals;
20 import static org.junit.Assert.assertNotNull;
21 import static org.junit.Assert.assertTrue;
22 
23 import android.graphics.Typeface;
24 import android.os.Build;
25 import android.text.SpannableString;
26 import android.text.Spanned;
27 import android.text.style.StyleSpan;
28 
29 import androidx.test.filters.SmallTest;
30 import androidx.test.runner.AndroidJUnit4;
31 
32 import org.junit.Test;
33 import org.junit.runner.RunWith;
34 
35 import java.util.Locale;
36 
37 @SmallTest
38 @RunWith(AndroidJUnit4.class)
39 public class LocaleSpanCompatUtilsTests {
40     @Test
testInstantiatable()41     public void testInstantiatable() {
42         if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1) {
43             // LocaleSpan isn't yet available.
44             return;
45         }
46         assertTrue(LocaleSpanCompatUtils.isLocaleSpanAvailable());
47         final Object japaneseLocaleSpan = LocaleSpanCompatUtils.newLocaleSpan(Locale.JAPANESE);
48         assertNotNull(japaneseLocaleSpan);
49         assertEquals(Locale.JAPANESE,
50                 LocaleSpanCompatUtils.getLocaleFromLocaleSpan(japaneseLocaleSpan));
51     }
52 
assertLocaleSpan(final Spanned spanned, final int index, final int expectedStart, final int expectedEnd, final Locale expectedLocale, final int expectedSpanFlags)53     private static void assertLocaleSpan(final Spanned spanned, final int index,
54             final int expectedStart, final int expectedEnd,
55             final Locale expectedLocale, final int expectedSpanFlags) {
56         final Object span = spanned.getSpans(0, spanned.length(), Object.class)[index];
57         assertEquals(expectedLocale, LocaleSpanCompatUtils.getLocaleFromLocaleSpan(span));
58         assertEquals(expectedStart, spanned.getSpanStart(span));
59         assertEquals(expectedEnd, spanned.getSpanEnd(span));
60         assertEquals(expectedSpanFlags, spanned.getSpanFlags(span));
61     }
62 
assertSpanEquals(final Object expectedSpan, final Spanned spanned, final int index)63     private static void assertSpanEquals(final Object expectedSpan, final Spanned spanned,
64             final int index) {
65         final Object[] spans = spanned.getSpans(0, spanned.length(), Object.class);
66         assertEquals(expectedSpan, spans[index]);
67     }
68 
assertSpanCount(final int expectedCount, final Spanned spanned)69     private static void assertSpanCount(final int expectedCount, final Spanned spanned) {
70         final Object[] spans = spanned.getSpans(0, spanned.length(), Object.class);
71         assertEquals(expectedCount, spans.length);
72     }
73 
74     @Test
testUpdateLocaleSpan()75     public void testUpdateLocaleSpan() {
76         if (!LocaleSpanCompatUtils.isLocaleSpanAvailable()) {
77             return;
78         }
79 
80         // Test if the simplest case works.
81         {
82             final SpannableString text = new SpannableString("0123456789");
83             LocaleSpanCompatUtils.updateLocaleSpan(text, 1, 5, Locale.JAPANESE);
84             assertSpanCount(1, text);
85             assertLocaleSpan(text, 0, 1, 5, Locale.JAPANESE, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
86         }
87 
88         // Test if only LocaleSpans are updated.
89         {
90             final SpannableString text = new SpannableString("0123456789");
91             final StyleSpan styleSpan = new StyleSpan(Typeface.BOLD);
92             text.setSpan(styleSpan, 0, 7, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
93             LocaleSpanCompatUtils.updateLocaleSpan(text, 1, 5, Locale.JAPANESE);
94             assertSpanCount(2, text);
95             assertSpanEquals(styleSpan, text, 0);
96             assertLocaleSpan(text, 1, 1, 5, Locale.JAPANESE, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
97         }
98 
99         // Test if two jointed spans are merged into one span.
100         {
101             final SpannableString text = new SpannableString("0123456789");
102             text.setSpan(LocaleSpanCompatUtils.newLocaleSpan(Locale.JAPANESE), 1, 3,
103                     Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
104             LocaleSpanCompatUtils.updateLocaleSpan(text, 3, 5, Locale.JAPANESE);
105             assertSpanCount(1, text);
106             assertLocaleSpan(text, 0, 1, 5, Locale.JAPANESE, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
107         }
108 
109         // Test if two overlapped spans are merged into one span.
110         {
111             final SpannableString text = new SpannableString("0123456789");
112             text.setSpan(LocaleSpanCompatUtils.newLocaleSpan(Locale.JAPANESE), 1, 4,
113                     Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
114             LocaleSpanCompatUtils.updateLocaleSpan(text, 3, 5, Locale.JAPANESE);
115             assertSpanCount(1, text);
116             assertLocaleSpan(text, 0, 1, 5, Locale.JAPANESE, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
117         }
118 
119         // Test if three overlapped spans are merged into one span.
120         {
121             final SpannableString text = new SpannableString("0123456789");
122             text.setSpan(LocaleSpanCompatUtils.newLocaleSpan(Locale.JAPANESE), 1, 4,
123                     Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
124             text.setSpan(LocaleSpanCompatUtils.newLocaleSpan(Locale.JAPANESE), 5, 6,
125                     Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
126             LocaleSpanCompatUtils.updateLocaleSpan(text, 2, 8, Locale.JAPANESE);
127             assertSpanCount(1, text);
128             assertLocaleSpan(text, 0, 1, 8, Locale.JAPANESE, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
129         }
130 
131         // Test if disjoint spans remain disjoint.
132         {
133             final SpannableString text = new SpannableString("0123456789");
134             text.setSpan(LocaleSpanCompatUtils.newLocaleSpan(Locale.JAPANESE), 1, 3,
135                     Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
136             text.setSpan(LocaleSpanCompatUtils.newLocaleSpan(Locale.JAPANESE), 5, 6,
137                     Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
138             LocaleSpanCompatUtils.updateLocaleSpan(text, 8, 9, Locale.JAPANESE);
139             assertSpanCount(3, text);
140             assertLocaleSpan(text, 0, 1, 3, Locale.JAPANESE, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
141             assertLocaleSpan(text, 1, 5, 6, Locale.JAPANESE, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
142             assertLocaleSpan(text, 2, 8, 9, Locale.JAPANESE, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
143         }
144 
145         // Test if existing span flags are preserved during merge.
146         {
147             final SpannableString text = new SpannableString("0123456789");
148             text.setSpan(LocaleSpanCompatUtils.newLocaleSpan(Locale.JAPANESE), 1, 5,
149                     Spanned.SPAN_INCLUSIVE_INCLUSIVE | Spanned.SPAN_INTERMEDIATE);
150             LocaleSpanCompatUtils.updateLocaleSpan(text, 3, 4, Locale.JAPANESE);
151             assertSpanCount(1, text);
152             assertLocaleSpan(text, 0, 1, 5, Locale.JAPANESE,
153                     Spanned.SPAN_INCLUSIVE_INCLUSIVE | Spanned.SPAN_INTERMEDIATE);
154         }
155 
156         // Test if existing span flags are preserved even when partially overlapped (leading edge).
157         {
158             final SpannableString text = new SpannableString("0123456789");
159             text.setSpan(LocaleSpanCompatUtils.newLocaleSpan(Locale.JAPANESE), 1, 5,
160                     Spanned.SPAN_INCLUSIVE_INCLUSIVE | Spanned.SPAN_INTERMEDIATE);
161             LocaleSpanCompatUtils.updateLocaleSpan(text, 3, 7, Locale.JAPANESE);
162             assertSpanCount(1, text);
163             assertLocaleSpan(text, 0, 1, 7, Locale.JAPANESE,
164                     Spanned.SPAN_INCLUSIVE_EXCLUSIVE | Spanned.SPAN_INTERMEDIATE);
165         }
166 
167         // Test if existing span flags are preserved even when partially overlapped (trailing edge).
168         {
169             final SpannableString text = new SpannableString("0123456789");
170             text.setSpan(LocaleSpanCompatUtils.newLocaleSpan(Locale.JAPANESE), 3, 7,
171                     Spanned.SPAN_INCLUSIVE_INCLUSIVE | Spanned.SPAN_INTERMEDIATE);
172             LocaleSpanCompatUtils.updateLocaleSpan(text, 1, 5, Locale.JAPANESE);
173             assertSpanCount(1, text);
174             assertLocaleSpan(text, 0, 1, 7, Locale.JAPANESE,
175                     Spanned.SPAN_EXCLUSIVE_INCLUSIVE | Spanned.SPAN_INTERMEDIATE);
176         }
177 
178         // Test if existing locale span will be removed when the locale doesn't match.
179         {
180             final SpannableString text = new SpannableString("0123456789");
181             text.setSpan(LocaleSpanCompatUtils.newLocaleSpan(Locale.ENGLISH), 3, 5,
182                     Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
183             LocaleSpanCompatUtils.updateLocaleSpan(text, 1, 7, Locale.JAPANESE);
184             assertSpanCount(1, text);
185             assertLocaleSpan(text, 0, 1, 7, Locale.JAPANESE, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
186         }
187 
188         // Test if existing locale span will be removed when the locale doesn't match. (case 2)
189         {
190             final SpannableString text = new SpannableString("0123456789");
191             text.setSpan(LocaleSpanCompatUtils.newLocaleSpan(Locale.ENGLISH), 3, 7,
192                     Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
193             LocaleSpanCompatUtils.updateLocaleSpan(text, 5, 6, Locale.JAPANESE);
194             assertSpanCount(3, text);
195             assertLocaleSpan(text, 0, 3, 5, Locale.ENGLISH, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
196             assertLocaleSpan(text, 1, 6, 7, Locale.ENGLISH, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
197             assertLocaleSpan(text, 2, 5, 6, Locale.JAPANESE, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
198         }
199 
200         // Test if existing locale span will be removed when the locale doesn't match. (case 3)
201         {
202             final SpannableString text = new SpannableString("0123456789");
203             text.setSpan(LocaleSpanCompatUtils.newLocaleSpan(Locale.ENGLISH), 3, 7,
204                     Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
205             LocaleSpanCompatUtils.updateLocaleSpan(text, 2, 5, Locale.JAPANESE);
206             assertSpanCount(2, text);
207             assertLocaleSpan(text, 0, 5, 7, Locale.ENGLISH, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
208             assertLocaleSpan(text, 1, 2, 5, Locale.JAPANESE, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
209         }
210 
211         // Test if existing locale span will be removed when the locale doesn't match. (case 3)
212         {
213             final SpannableString text = new SpannableString("0123456789");
214             text.setSpan(LocaleSpanCompatUtils.newLocaleSpan(Locale.ENGLISH), 3, 7,
215                     Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
216             LocaleSpanCompatUtils.updateLocaleSpan(text, 5, 8, Locale.JAPANESE);
217             assertSpanCount(2, text);
218             assertLocaleSpan(text, 0, 3, 5, Locale.ENGLISH, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
219             assertLocaleSpan(text, 1, 5, 8, Locale.JAPANESE, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
220         }
221     }
222 }
223