• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2022 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.view.inputmethod.cts;
18 
19 import static org.junit.Assert.assertEquals;
20 import static org.junit.Assert.assertThrows;
21 import static org.junit.Assert.assertTrue;
22 import static org.junit.Assert.fail;
23 
24 import android.graphics.Matrix;
25 import android.graphics.RectF;
26 import android.os.Parcel;
27 import android.text.SegmentFinder;
28 import android.view.inputmethod.TextBoundsInfo;
29 import android.view.inputmethod.TextBoundsInfoResult;
30 
31 import androidx.test.filters.SmallTest;
32 import androidx.test.runner.AndroidJUnit4;
33 
34 import com.android.compatibility.common.util.ApiTest;
35 
36 import org.junit.Test;
37 import org.junit.runner.RunWith;
38 
39 @SmallTest
40 @RunWith(AndroidJUnit4.class)
41 public class TextBoundsInfoTest {
42     private static final float[] CHARACTER_BOUNDS1 = new float[] {
43             0.0f, 0.0f, 10.0f, 20.0f,
44             10.0f, 0.0f, 20.0f, 20.0f,
45             20.0f, 0.0f, 30.0f, 20.0f,
46             10.0f, 20.0f, 20.0f, 40.0f,
47             0.0f, 20.0f, 10.0f, 40.0f};
48 
49     private static final float[] CHARACTER_BOUNDS2 = new float[] {
50             20.0f, 0.0f, 30.0f, 20.0f,
51             10.0f, 0.0f, 20.0f, 20.0f,
52             0.0f, 0.0f, 10.0f, 20.0f,
53             0.0f, 20.0f, 10.0f, 40.0f,
54             10.0f, 20.0f, 20.0f, 40.0f};
55 
56     private static final int LTR_BIDI_LEVEL = 0;
57     private static final int RTL_BIDI_LEVEL = 1;
58     private static final int MAX_BIDI_LEVEL = 125;
59     private static final int[] CHARACTER_BIDI_LEVEL1 = new int[] {LTR_BIDI_LEVEL, LTR_BIDI_LEVEL,
60             LTR_BIDI_LEVEL, RTL_BIDI_LEVEL, MAX_BIDI_LEVEL};
61     private static final int[] CHARACTER_BIDI_LEVEL2 = new int[] {MAX_BIDI_LEVEL, RTL_BIDI_LEVEL,
62             LTR_BIDI_LEVEL, LTR_BIDI_LEVEL, LTR_BIDI_LEVEL};
63     private static final int[] CHARACTER_FLAGS1 = new int[] {
64             TextBoundsInfo.FLAG_CHARACTER_WHITESPACE,
65             TextBoundsInfo.FLAG_CHARACTER_PUNCTUATION,
66             TextBoundsInfo.FLAG_CHARACTER_LINEFEED,
67             TextBoundsInfo.FLAG_LINE_IS_RTL,
68             TextBoundsInfo.FLAG_LINE_IS_RTL
69     };
70 
71     private static final int[] CHARACTER_FLAGS2 = new int[] {TextBoundsInfo.FLAG_LINE_IS_RTL,
72             TextBoundsInfo.FLAG_LINE_IS_RTL, TextBoundsInfo.FLAG_CHARACTER_WHITESPACE, 0, 0};
73 
74     private static final SegmentFinder GRAPHEME_SEGMENT_FINDER1 =
75             new SegmentFinder.PrescribedSegmentFinder(
76                     new int[] { 0, 1, 1, 2, 2, 4, 4, 5, 5, 7, 7, 8, 8, 9, 9, 10, 10, 11 });
77 
78     private static final SegmentFinder GRAPHEME_SEGMENT_FINDER2 =
79             new SegmentFinder.PrescribedSegmentFinder(
80                     new int[] { 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10 });
81 
82     private static final SegmentFinder WORD_SEGMENT_FINDER1 =
83             new SegmentFinder.PrescribedSegmentFinder(new int[] { 0, 3, 4, 5, 5, 10 });
84     private static final SegmentFinder WORD_SEGMENT_FINDER2 =
85             new SegmentFinder.PrescribedSegmentFinder(new int[] { 0, 4, 5, 8, 9, 11 });
86 
87 
88     private static final SegmentFinder LINE_SEGMENT_FINDER1 =
89             new SegmentFinder.PrescribedSegmentFinder(new int[] { 0, 5, 5, 8, 8, 10, 10, 15 });
90     private static final SegmentFinder LINE_SEGMENT_FINDER2 =
91             new SegmentFinder.PrescribedSegmentFinder(new int[] { 0, 5, 5, 7, 7, 10, 10, 15 });
92 
93     @Test
94     @ApiTest(
95             apis = {
96                     "android.view.inputmethod.TextBoundsInfo#getStart",
97                     "android.view.inputmethod.TextBoundsInfo#getEnd",
98                     "android.view.inputmethod.TextBoundsInfo#getMatrix",
99                     "android.view.inputmethod.TextBoundsInfo#getCharacterBounds",
100                     "android.view.inputmethod.TextBoundsInfo#getCharacterFlags",
101                     "android.view.inputmethod.TextBoundsInfo#getCharacterBidiLevel",
102                     "android.view.inputmethod.TextBoundsInfo#getGraphemeSegmentFinder",
103                     "android.view.inputmethod.TextBoundsInfo#getWordSegmentFinder",
104                     "android.view.inputmethod.TextBoundsInfo#getLineSegmentFinder",
105                     "android.view.inputmethod.TextBoundsInfo.Builder#setStart",
106                     "android.view.inputmethod.TextBoundsInfo.Builder#setEnd",
107                     "android.view.inputmethod.TextBoundsInfo.Builder#setMatrix",
108                     "android.view.inputmethod.TextBoundsInfo.Builder#setCharacterBounds",
109                     "android.view.inputmethod.TextBoundsInfo.Builder#setCharacterFlags",
110                     "android.view.inputmethod.TextBoundsInfo.Builder#setCharacterBidiLevel",
111                     "android.view.inputmethod.TextBoundsInfo.Builder#setGraphemeSegmentFinder",
112                     "android.view.inputmethod.TextBoundsInfo.Builder#setWordSegmentFinder",
113                     "android.view.inputmethod.TextBoundsInfo.Builder#setLineSegmentFinder",
114                     "android.view.inputmethod.TextBoundsInfo.Builder#build",
115             }
116     )
testBuilder()117     public void testBuilder() {
118         final int start1 = 5;
119         final int end1 = 10;
120         final Matrix matrix1 = new Matrix();
121         matrix1.setRotate(10f);
122 
123         TextBoundsInfo.Builder builder = new TextBoundsInfo.Builder(start1, end1);
124         TextBoundsInfo textBoundsInfo1 = builder.setMatrix(matrix1)
125                 .setCharacterBounds(CHARACTER_BOUNDS1)
126                 .setCharacterBidiLevel(CHARACTER_BIDI_LEVEL1)
127                 .setCharacterFlags(CHARACTER_FLAGS1)
128                 .setGraphemeSegmentFinder(GRAPHEME_SEGMENT_FINDER1)
129                 .setWordSegmentFinder(WORD_SEGMENT_FINDER1)
130                 .setLineSegmentFinder(LINE_SEGMENT_FINDER1)
131                 .build();
132 
133         assertEquals(start1, textBoundsInfo1.getStartIndex());
134         assertEquals(end1, textBoundsInfo1.getEndIndex());
135         final Matrix actualMatrix = new Matrix();
136         textBoundsInfo1.getMatrix(actualMatrix);
137         assertEquals(matrix1, actualMatrix);
138         assertCharacterBounds(CHARACTER_BOUNDS1, textBoundsInfo1);
139         assertCharacterBidiLevel(CHARACTER_BIDI_LEVEL1, textBoundsInfo1);
140         assertCharacterFlags(CHARACTER_FLAGS1, textBoundsInfo1);
141         assertEquals(GRAPHEME_SEGMENT_FINDER1, textBoundsInfo1.getGraphemeSegmentFinder());
142         assertEquals(WORD_SEGMENT_FINDER1, textBoundsInfo1.getWordSegmentFinder());
143         assertEquals(LINE_SEGMENT_FINDER1, textBoundsInfo1.getLineSegmentFinder());
144 
145         // Build another TextBoundsInfo, making sure the Builder creates an identical object.
146         TextBoundsInfo textBoundsInfo2 = builder.build();
147         assertEquals(start1, textBoundsInfo2.getStartIndex());
148         assertEquals(end1, textBoundsInfo2.getEndIndex());
149         textBoundsInfo2.getMatrix(actualMatrix);
150         assertEquals(matrix1, actualMatrix);
151         assertCharacterBounds(CHARACTER_BOUNDS1, textBoundsInfo2);
152         assertCharacterBidiLevel(CHARACTER_BIDI_LEVEL1, textBoundsInfo2);
153         assertCharacterFlags(CHARACTER_FLAGS1, textBoundsInfo2);
154         assertEquals(GRAPHEME_SEGMENT_FINDER1, textBoundsInfo2.getGraphemeSegmentFinder());
155         assertEquals(WORD_SEGMENT_FINDER1, textBoundsInfo2.getWordSegmentFinder());
156         assertEquals(LINE_SEGMENT_FINDER1, textBoundsInfo2.getLineSegmentFinder());
157 
158         final int start2 = 8;
159         final int end2 = 13;
160         final Matrix matrix2 = new Matrix();
161         matrix2.setTranslate(20f, 30f);
162 
163         // Clear the existing parameters and build a different object.
164         TextBoundsInfo textBoundsInfo3 = builder.clear()
165                 .setStartAndEnd(start2, end2)
166                 .setMatrix(matrix2)
167                 .setCharacterBounds(CHARACTER_BOUNDS2)
168                 .setCharacterBidiLevel(CHARACTER_BIDI_LEVEL2)
169                 .setCharacterFlags(CHARACTER_FLAGS2)
170                 .setGraphemeSegmentFinder(GRAPHEME_SEGMENT_FINDER2)
171                 .setWordSegmentFinder(WORD_SEGMENT_FINDER2)
172                 .setLineSegmentFinder(LINE_SEGMENT_FINDER2)
173                 .build();
174 
175         assertEquals(start2, textBoundsInfo3.getStartIndex());
176         assertEquals(end2, textBoundsInfo3.getEndIndex());
177         textBoundsInfo3.getMatrix(actualMatrix);
178         assertEquals(matrix2, actualMatrix);
179         assertCharacterBounds(CHARACTER_BOUNDS2, textBoundsInfo3);
180         assertCharacterBidiLevel(CHARACTER_BIDI_LEVEL2, textBoundsInfo3);
181         assertCharacterFlags(CHARACTER_FLAGS2, textBoundsInfo3);
182         assertEquals(GRAPHEME_SEGMENT_FINDER2, textBoundsInfo3.getGraphemeSegmentFinder());
183         assertEquals(WORD_SEGMENT_FINDER2, textBoundsInfo3.getWordSegmentFinder());
184         assertEquals(LINE_SEGMENT_FINDER2, textBoundsInfo3.getLineSegmentFinder());
185     }
186 
187     @Test
188     @ApiTest(apis = { "android.view.inputmethod.TextBoundsInfo#writeToParcel" })
testTextBoundsInfo_writeToParcel()189     public void testTextBoundsInfo_writeToParcel() {
190         final Matrix matrix = new Matrix();
191         matrix.setRotate(15f);
192 
193         TextBoundsInfo textBoundsInfo1 = new TextBoundsInfo.Builder(5, 10)
194                 .setMatrix(matrix)
195                 .setCharacterBounds(CHARACTER_BOUNDS1)
196                 .setCharacterBidiLevel(CHARACTER_BIDI_LEVEL1)
197                 .setCharacterFlags(CHARACTER_FLAGS1)
198                 .setGraphemeSegmentFinder(GRAPHEME_SEGMENT_FINDER1)
199                 .setWordSegmentFinder(WORD_SEGMENT_FINDER1)
200                 .setLineSegmentFinder(LINE_SEGMENT_FINDER1)
201                 .build();
202 
203         // Gut check, making sure the first TextBoundsInfo is built correctly.
204         assertEquals(5, textBoundsInfo1.getStartIndex());
205         assertEquals(10, textBoundsInfo1.getEndIndex());
206         final Matrix actualMatrix = new Matrix();
207         textBoundsInfo1.getMatrix(actualMatrix);
208         assertEquals(matrix, actualMatrix);
209         assertCharacterBounds(CHARACTER_BOUNDS1, textBoundsInfo1);
210         assertCharacterFlags(CHARACTER_FLAGS1, textBoundsInfo1);
211         assertCharacterBidiLevel(CHARACTER_BIDI_LEVEL1, textBoundsInfo1);
212         assertEquals(GRAPHEME_SEGMENT_FINDER1, textBoundsInfo1.getGraphemeSegmentFinder());
213         assertEquals(WORD_SEGMENT_FINDER1, textBoundsInfo1.getWordSegmentFinder());
214         assertEquals(LINE_SEGMENT_FINDER1, textBoundsInfo1.getLineSegmentFinder());
215 
216         Parcel parcel = Parcel.obtain();
217         final int dataPosition = parcel.dataPosition();
218         parcel.writeParcelable(textBoundsInfo1, 0);
219         parcel.setDataPosition(dataPosition);
220 
221         TextBoundsInfo textBoundsInfo2 =
222                 parcel.readParcelable(TextBoundsInfo.class.getClassLoader(), TextBoundsInfo.class);
223 
224         assertEquals(5, textBoundsInfo2.getStartIndex());
225         assertEquals(10, textBoundsInfo2.getEndIndex());
226         textBoundsInfo2.getMatrix(actualMatrix);
227         assertEquals(matrix, actualMatrix);
228         assertCharacterBounds(CHARACTER_BOUNDS1, textBoundsInfo2);
229         assertCharacterFlags(CHARACTER_FLAGS1, textBoundsInfo1);
230         assertCharacterBidiLevel(CHARACTER_BIDI_LEVEL1, textBoundsInfo1);
231         // Only check the iterator in the given range.
232         assertSegmentFinderEqualsInRange(GRAPHEME_SEGMENT_FINDER1,
233                 textBoundsInfo2.getGraphemeSegmentFinder(), 5, 10);
234         assertSegmentFinderEqualsInRange(WORD_SEGMENT_FINDER1,
235                 textBoundsInfo2.getWordSegmentFinder(), 5, 10);
236         assertSegmentFinderEqualsInRange(LINE_SEGMENT_FINDER1,
237                 textBoundsInfo2.getLineSegmentFinder(), 5, 10);
238     }
239 
240 
241     @Test(expected = IllegalStateException.class)
242     @ApiTest(apis = { "android.view.inputmethod.TextBoundsInfo.Builder#build" })
testBuilder_matrix_isRequired()243     public void testBuilder_matrix_isRequired() {
244         new TextBoundsInfo.Builder(5, 10)
245                 .setCharacterBounds(CHARACTER_BOUNDS1)
246                 .setCharacterBidiLevel(CHARACTER_BIDI_LEVEL1)
247                 .setCharacterFlags(CHARACTER_FLAGS1)
248                 .setGraphemeSegmentFinder(GRAPHEME_SEGMENT_FINDER1)
249                 .setWordSegmentFinder(WORD_SEGMENT_FINDER2)
250                 .setLineSegmentFinder(LINE_SEGMENT_FINDER1)
251                 .build();
252     }
253 
254     @Test(expected = IllegalStateException.class)
255     @ApiTest(apis = { "android.view.inputmethod.TextBoundsInfo.Builder#build" })
testBuilder_startAndEnd_isRequired()256     public void testBuilder_startAndEnd_isRequired() {
257         final TextBoundsInfo.Builder builder = new TextBoundsInfo.Builder(5, 10);
258         builder.clear();
259         builder.setMatrix(Matrix.IDENTITY_MATRIX)
260                 .setCharacterBounds(CHARACTER_BOUNDS1)
261                 .setCharacterBidiLevel(CHARACTER_BIDI_LEVEL1)
262                 .setCharacterFlags(CHARACTER_FLAGS1)
263                 .setGraphemeSegmentFinder(GRAPHEME_SEGMENT_FINDER1)
264                 .setWordSegmentFinder(WORD_SEGMENT_FINDER2)
265                 .setLineSegmentFinder(LINE_SEGMENT_FINDER1)
266                 .build();
267     }
268 
269     @Test(expected = IllegalArgumentException.class)
270     @ApiTest(apis = { "android.view.inputmethod.TextBoundsInfo.Builder#setStart" })
testBuilder_start_isNegative()271     public void testBuilder_start_isNegative() {
272         new TextBoundsInfo.Builder(-1 , 5);
273     }
274 
275     @Test(expected = IllegalArgumentException.class)
276     @ApiTest(apis = { "android.view.inputmethod.TextBoundsInfo.Builder#setEnd" })
testBuilder_end_isNegative()277     public void testBuilder_end_isNegative() {
278         new TextBoundsInfo.Builder(0, -1);
279     }
280 
281     @Test(expected = IllegalArgumentException.class)
282     @ApiTest(apis = { "android.view.inputmethod.TextBoundsInfo.Builder#build" })
testBuilder_startGreaterThanEnd()283     public void testBuilder_startGreaterThanEnd() {
284         new TextBoundsInfo.Builder(1, 0);
285     }
286 
287     @Test(expected = IllegalStateException.class)
288     @ApiTest(apis = { "android.view.inputmethod.TextBoundsInfo.Builder#build" })
testBuilder_characterBounds_isRequired()289     public void testBuilder_characterBounds_isRequired() {
290         new TextBoundsInfo.Builder(5, 10)
291                 .setMatrix(Matrix.IDENTITY_MATRIX)
292                 .setCharacterFlags(CHARACTER_FLAGS1)
293                 .setCharacterBidiLevel(CHARACTER_BIDI_LEVEL1)
294                 .setGraphemeSegmentFinder(GRAPHEME_SEGMENT_FINDER1)
295                 .setWordSegmentFinder(WORD_SEGMENT_FINDER2)
296                 .setLineSegmentFinder(LINE_SEGMENT_FINDER1)
297                 .build();
298     }
299 
300     @Test(expected = NullPointerException.class)
301     @ApiTest(apis = { "android.view.inputmethod.TextBoundsInfo.Builder#setCharacterBounds" })
testBuilder_characterBounds_isNull()302     public void testBuilder_characterBounds_isNull() {
303         new TextBoundsInfo.Builder(0, 5).setCharacterBounds(null);
304     }
305 
306     @Test(expected = IllegalStateException.class)
307     @ApiTest(apis = { "android.view.inputmethod.TextBoundsInfo.Builder#build" })
testBuilder_characterBounds_wrongLength()308     public void testBuilder_characterBounds_wrongLength() {
309         // Expected characterBounds.length == 20 to match the given range [0, 5).
310         new TextBoundsInfo.Builder(5, 10)
311                 .setMatrix(Matrix.IDENTITY_MATRIX)
312                 .setCharacterBounds(new float[16])
313                 .setCharacterFlags(CHARACTER_FLAGS1)
314                 .setCharacterBidiLevel(CHARACTER_BIDI_LEVEL1)
315                 .setGraphemeSegmentFinder(GRAPHEME_SEGMENT_FINDER1)
316                 .setWordSegmentFinder(WORD_SEGMENT_FINDER2)
317                 .setLineSegmentFinder(LINE_SEGMENT_FINDER1)
318                 .build();
319     }
320 
321     @Test(expected = IllegalStateException.class)
322     @ApiTest(apis = { "android.view.inputmethod.TextBoundsInfo.Builder#build" })
testBuilder_characterFlags_isRequired()323     public void testBuilder_characterFlags_isRequired() {
324         new TextBoundsInfo.Builder(5, 10)
325                 .setMatrix(Matrix.IDENTITY_MATRIX)
326                 .setCharacterBounds(CHARACTER_BOUNDS1)
327                 .setCharacterBidiLevel(CHARACTER_BIDI_LEVEL1)
328                 .setGraphemeSegmentFinder(GRAPHEME_SEGMENT_FINDER1)
329                 .setWordSegmentFinder(WORD_SEGMENT_FINDER2)
330                 .setLineSegmentFinder(LINE_SEGMENT_FINDER1)
331                 .build();
332     }
333 
334     @Test(expected = NullPointerException.class)
335     @ApiTest(apis = { "android.view.inputmethod.TextBoundsInfo.Builder#setCharacterFlags" })
testBuilder_characterFlags_isNull()336     public void testBuilder_characterFlags_isNull() {
337         new TextBoundsInfo.Builder(5, 10).setCharacterFlags(null);
338     }
339 
340     @Test(expected = IllegalStateException.class)
341     @ApiTest(apis = { "android.view.inputmethod.TextBoundsInfo.Builder#build" })
testBuilder_characterFlags_wrongLength()342     public void testBuilder_characterFlags_wrongLength() {
343         // Expected characterFlags.length == 5 to match the given range [0, 5).
344         new TextBoundsInfo.Builder(5, 10)
345                 .setMatrix(Matrix.IDENTITY_MATRIX)
346                 .setCharacterFlags(new int[6])
347                 .setCharacterBidiLevel(CHARACTER_BIDI_LEVEL1)
348                 .setCharacterBounds(CHARACTER_BOUNDS1)
349                 .setGraphemeSegmentFinder(GRAPHEME_SEGMENT_FINDER1)
350                 .setWordSegmentFinder(WORD_SEGMENT_FINDER2)
351                 .setLineSegmentFinder(LINE_SEGMENT_FINDER1)
352                 .build();
353     }
354 
355     @Test(expected = IllegalStateException.class)
356     @ApiTest(apis = { "android.view.inputmethod.TextBoundsInfo.Builder#build" })
testBuilder_characterBidiLevel_isRequired()357     public void testBuilder_characterBidiLevel_isRequired() {
358         new TextBoundsInfo.Builder(5, 10)
359                 .setMatrix(Matrix.IDENTITY_MATRIX)
360                 .setCharacterBounds(CHARACTER_BOUNDS1)
361                 .setCharacterFlags(CHARACTER_FLAGS1)
362                 .setGraphemeSegmentFinder(GRAPHEME_SEGMENT_FINDER1)
363                 .setWordSegmentFinder(WORD_SEGMENT_FINDER2)
364                 .setLineSegmentFinder(LINE_SEGMENT_FINDER1)
365                 .build();
366     }
367 
368     @Test(expected = NullPointerException.class)
369     @ApiTest(apis = { "android.view.inputmethod.TextBoundsInfo.Builder#setCharacterBidiLevel" })
testBuilder_characterBidiLevel_isNull()370     public void testBuilder_characterBidiLevel_isNull() {
371         new TextBoundsInfo.Builder(5, 10).setCharacterBidiLevel(null);
372     }
373 
374     @Test(expected = IllegalStateException.class)
375     @ApiTest(apis = { "android.view.inputmethod.TextBoundsInfo.Builder#build" })
testBuilder_characterBidiLevel_wrongLength()376     public void testBuilder_characterBidiLevel_wrongLength() {
377         // Expected characterBidiLevel.length == 5 to match the given range [0, 5).
378         new TextBoundsInfo.Builder(5, 10)
379                 .setMatrix(Matrix.IDENTITY_MATRIX)
380                 .setCharacterFlags(CHARACTER_FLAGS1)
381                 .setCharacterBidiLevel(new int[6])
382                 .setCharacterBounds(CHARACTER_BOUNDS1)
383                 .setGraphemeSegmentFinder(GRAPHEME_SEGMENT_FINDER1)
384                 .setWordSegmentFinder(WORD_SEGMENT_FINDER2)
385                 .setLineSegmentFinder(LINE_SEGMENT_FINDER1)
386                 .build();
387     }
388 
389     @Test
390     @ApiTest(apis = { "android.view.inputmethod.TextBoundsInfo.Builder#setCharacterBidiLevel" })
testBuilder_characterBidiLevel_invalidBidiLevel()391     public void testBuilder_characterBidiLevel_invalidBidiLevel() {
392         // Bidi level must be in the range of [0, 125].
393         try {
394             new TextBoundsInfo.Builder(0, 1).setCharacterBidiLevel(new int[] { 126 });
395             fail();
396         } catch (IllegalArgumentException ignored) { }
397 
398         try {
399             new TextBoundsInfo.Builder(0 , 1).setCharacterBidiLevel(new int[] { -1 });
400             fail();
401         } catch (IllegalArgumentException ignored) { }
402     }
403 
404     @Test
405     @ApiTest(apis = { "android.view.inputmethod.TextBoundsInfo.Builder#setCharacterBidiLevel" })
testBuilder_characterBidiLevel_isCleared()406     public void testBuilder_characterBidiLevel_isCleared() {
407         final var builder = new TextBoundsInfo.Builder(5, 10)
408                 .setMatrix(Matrix.IDENTITY_MATRIX)
409                 .setCharacterFlags(CHARACTER_FLAGS1)
410                 .setCharacterBidiLevel(CHARACTER_BIDI_LEVEL1)
411                 .setCharacterBounds(CHARACTER_BOUNDS1)
412                 .setGraphemeSegmentFinder(GRAPHEME_SEGMENT_FINDER1)
413                 .setWordSegmentFinder(WORD_SEGMENT_FINDER2)
414                 .setLineSegmentFinder(LINE_SEGMENT_FINDER1);
415         builder.build();
416 
417         builder.clear();
418         builder.setMatrix(Matrix.IDENTITY_MATRIX)
419                 .setStartAndEnd(5, 10)
420                 .setCharacterFlags(CHARACTER_FLAGS1)
421                 // omit setting characterBidiLevel.
422                 .setCharacterBounds(CHARACTER_BOUNDS1)
423                 .setGraphemeSegmentFinder(GRAPHEME_SEGMENT_FINDER1)
424                 .setWordSegmentFinder(WORD_SEGMENT_FINDER2)
425                 .setLineSegmentFinder(LINE_SEGMENT_FINDER1);
426 
427         assertThrows(IllegalStateException.class, builder::build);
428     }
429 
430     @Test(expected = IllegalArgumentException.class)
431     @ApiTest(apis = { "android.view.inputmethod.TextBoundsInfo.Builder#setCharacterFlags" })
testBuilder_characterFlags_invalidFlag()432     public void testBuilder_characterFlags_invalidFlag() {
433         TextBoundsInfo.Builder builder = new TextBoundsInfo.Builder(0, 5);
434 
435         // 1 << 20 is an unknown flags
436         int[] characterFlags = new int[] { 0, 1 << 20, 0, 0, 0};
437         builder.setCharacterFlags(characterFlags);
438     }
439 
440     @Test(expected = IllegalStateException.class)
441     @ApiTest(apis = { "android.view.inputmethod.TextBoundsInfo.Builder#build" })
testBuilder_graphemeSegmentFinder_isRequired()442     public void testBuilder_graphemeSegmentFinder_isRequired() {
443         new TextBoundsInfo.Builder(5, 10)
444                 .setMatrix(Matrix.IDENTITY_MATRIX)
445                 .setCharacterBounds(CHARACTER_BOUNDS1)
446                 .setCharacterBidiLevel(CHARACTER_BIDI_LEVEL1)
447                 .setCharacterFlags(CHARACTER_FLAGS1)
448                 .setWordSegmentFinder(WORD_SEGMENT_FINDER2)
449                 .setLineSegmentFinder(LINE_SEGMENT_FINDER1)
450                 .build();
451     }
452 
453     @Test(expected = NullPointerException.class)
454     @ApiTest(apis = { "android.view.inputmethod.TextBoundsInfo.Builder#setGraphemeSegmentFinder" })
testBuilder_graphemeSegmentFinder_isNull()455     public void testBuilder_graphemeSegmentFinder_isNull() {
456         new TextBoundsInfo.Builder(5, 10).setGraphemeSegmentFinder(null);
457     }
458 
459     @Test(expected = IllegalStateException.class)
460     @ApiTest(apis = { "android.view.inputmethod.TextBoundsInfo.Builder#build" })
testBuilder_wordSegmentFinder_isRequired()461     public void testBuilder_wordSegmentFinder_isRequired() {
462         new TextBoundsInfo.Builder(5, 10)
463                 .setMatrix(Matrix.IDENTITY_MATRIX)
464                 .setCharacterBounds(CHARACTER_BOUNDS1)
465                 .setCharacterBidiLevel(CHARACTER_BIDI_LEVEL1)
466                 .setCharacterFlags(CHARACTER_FLAGS1)
467                 .setGraphemeSegmentFinder(GRAPHEME_SEGMENT_FINDER1)
468                 .setLineSegmentFinder(LINE_SEGMENT_FINDER1)
469                 .build();
470     }
471 
472     @Test(expected = NullPointerException.class)
473     @ApiTest(apis = { "android.view.inputmethod.TextBoundsInfo.Builder#setWordSegmentFinder" })
testBuilder_wordSegmentFinder_isNull()474     public void testBuilder_wordSegmentFinder_isNull() {
475         new TextBoundsInfo.Builder(5, 10).setWordSegmentFinder(null);
476     }
477 
478     @Test(expected = IllegalStateException.class)
479     @ApiTest(apis = { "android.view.inputmethod.TextBoundsInfo.Builder#build" })
testBuilder_lineSegmentFinder_isRequired()480     public void testBuilder_lineSegmentFinder_isRequired() {
481         new TextBoundsInfo.Builder(5, 10)
482                 .setMatrix(Matrix.IDENTITY_MATRIX)
483                 .setCharacterBounds(CHARACTER_BOUNDS1)
484                 .setCharacterBidiLevel(CHARACTER_BIDI_LEVEL1)
485                 .setCharacterFlags(CHARACTER_FLAGS1)
486                 .setGraphemeSegmentFinder(GRAPHEME_SEGMENT_FINDER1)
487                 .setWordSegmentFinder(WORD_SEGMENT_FINDER2)
488                 .build();
489     }
490 
491     @Test(expected = NullPointerException.class)
492     @ApiTest(apis = { "android.view.inputmethod.TextBoundsInfo.Builder#setLineSegmentFinder" })
testBuilder_lineSegmentFinder_isNull()493     public void testBuilder_lineSegmentFinder_isNull() {
494         new TextBoundsInfo.Builder(5, 10).setLineSegmentFinder(null);
495     }
496 
497     @Test
498     @ApiTest(apis = { "android.view.inputmethod.TextBoundsInfoResult#getResultCode",
499             "android.view.inputmethod.TextBoundsInfoResult#getTextBoundsInfo"})
testTextBoundsInfoResult_constructor()500     public void testTextBoundsInfoResult_constructor() {
501         TextBoundsInfoResult result =
502                 new TextBoundsInfoResult(TextBoundsInfoResult.CODE_UNSUPPORTED);
503         assertEquals(result.getResultCode(), TextBoundsInfoResult.CODE_UNSUPPORTED);
504 
505         result = new TextBoundsInfoResult(TextBoundsInfoResult.CODE_FAILED);
506         assertEquals(result.getResultCode(), TextBoundsInfoResult.CODE_FAILED);
507 
508         result = new TextBoundsInfoResult(TextBoundsInfoResult.CODE_CANCELLED);
509         assertEquals(result.getResultCode(), TextBoundsInfoResult.CODE_CANCELLED);
510 
511         final TextBoundsInfo textBoundsInfo = new TextBoundsInfo.Builder(5, 10)
512                 .setCharacterBounds(CHARACTER_BOUNDS1)
513                 .setCharacterBidiLevel(CHARACTER_BIDI_LEVEL1)
514                 .setMatrix(Matrix.IDENTITY_MATRIX)
515                 .setCharacterFlags(CHARACTER_FLAGS1)
516                 .setGraphemeSegmentFinder(GRAPHEME_SEGMENT_FINDER1)
517                 .setWordSegmentFinder(WORD_SEGMENT_FINDER1)
518                 .setLineSegmentFinder(LINE_SEGMENT_FINDER1)
519                 .build();
520 
521         result = new TextBoundsInfoResult(TextBoundsInfoResult.CODE_SUCCESS,
522                         textBoundsInfo);
523         assertEquals(result.getResultCode(), TextBoundsInfoResult.CODE_SUCCESS);
524         assertEquals(result.getTextBoundsInfo(), textBoundsInfo);
525     }
526 
527     @Test
testTextBoundsInfoResult_resultCodeIsSuccess_textBoundsInfoIsRequired()528     public void testTextBoundsInfoResult_resultCodeIsSuccess_textBoundsInfoIsRequired() {
529         try {
530             new TextBoundsInfoResult(TextBoundsInfoResult.CODE_SUCCESS);
531             fail();
532         } catch (IllegalStateException ignored) { }
533 
534         try {
535             new TextBoundsInfoResult(TextBoundsInfoResult.CODE_SUCCESS, null);
536             fail();
537         } catch (IllegalStateException ignored) { }
538 
539     }
540 
assertCharacterBounds(float[] characterBounds, TextBoundsInfo textBoundsInfo)541     private static void assertCharacterBounds(float[] characterBounds,
542             TextBoundsInfo textBoundsInfo) {
543         final int start = textBoundsInfo.getStartIndex();
544         final int end = textBoundsInfo.getEndIndex();
545         for (int offset = 0; offset < end - start; ++offset) {
546             final RectF expectedRect = new RectF(
547                     characterBounds[4 * offset],
548                     characterBounds[4 * offset + 1],
549                     characterBounds[4 * offset + 2],
550                     characterBounds[4 * offset + 3]);
551 
552             final RectF actualRectF = new RectF();
553             textBoundsInfo.getCharacterBounds(offset + start, actualRectF);
554             assertEquals(expectedRect, actualRectF);
555         }
556     }
557 
assertCharacterFlags(int[] characterFlags, TextBoundsInfo textBoundsInfo)558     private static void assertCharacterFlags(int[] characterFlags,
559             TextBoundsInfo textBoundsInfo) {
560         final int start = textBoundsInfo.getStartIndex();
561         final int end = textBoundsInfo.getEndIndex();
562         for (int offset = 0; offset < end - start; ++offset) {
563             assertEquals(characterFlags[offset],
564                     textBoundsInfo.getCharacterFlags(offset + start));
565         }
566     }
567 
assertCharacterBidiLevel(int[] characterBidiLevels, TextBoundsInfo textBoundsInfo)568     private static void assertCharacterBidiLevel(int[] characterBidiLevels,
569             TextBoundsInfo textBoundsInfo) {
570         final int start = textBoundsInfo.getStartIndex();
571         final int end = textBoundsInfo.getEndIndex();
572         for (int offset = 0; offset < end - start; ++offset) {
573             assertEquals(characterBidiLevels[offset],
574                     textBoundsInfo.getCharacterBidiLevel(offset + start));
575         }
576     }
577 
578     /**
579      * Helper method to assert that the given {@link SegmentFinder}s are the same in the given
580      * range.
581      */
assertSegmentFinderEqualsInRange(SegmentFinder expect, SegmentFinder actual, int start, int end)582     private static void assertSegmentFinderEqualsInRange(SegmentFinder expect,
583             SegmentFinder actual, int start, int end) {
584         int expectSegmentEnd = expect.nextEndBoundary(start);
585         int expectSegmentStart = expect.previousStartBoundary(expectSegmentEnd);
586 
587         int actualSegmentEnd = actual.nextEndBoundary(start);
588         int actualSegmentStart = actual.previousStartBoundary(actualSegmentEnd);
589 
590         while (expectSegmentStart != SegmentFinder.DONE && expectSegmentEnd <= end) {
591             assertEquals(expectSegmentStart, actualSegmentStart);
592             assertEquals(expectSegmentEnd, actualSegmentEnd);
593 
594             expectSegmentStart = expect.nextStartBoundary(expectSegmentStart);
595             actualSegmentStart = actual.nextStartBoundary(actualSegmentStart);
596 
597             expectSegmentEnd = expect.nextEndBoundary(expectSegmentEnd);
598             actualSegmentEnd = actual.nextEndBoundary(actualSegmentEnd);
599         }
600 
601         // The actual SegmentFinder doesn't have more segments within the range.
602         assertTrue(actualSegmentEnd > end || actualSegmentEnd == SegmentFinder.DONE);
603     }
604 }
605