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
17package android.view.inputmethod;
18
19import static android.view.inputmethod.CursorAnchorInfo.FLAG_HAS_INVISIBLE_REGION;
20import static android.view.inputmethod.CursorAnchorInfo.FLAG_HAS_VISIBLE_REGION;
21import static android.view.inputmethod.CursorAnchorInfo.FLAG_IS_RTL;
22
23import static org.junit.Assert.assertEquals;
24import static org.junit.Assert.assertFalse;
25import static org.junit.Assert.assertNull;
26import static org.junit.Assert.assertTrue;
27
28import android.graphics.Matrix;
29import android.graphics.RectF;
30import android.os.Parcel;
31import android.support.test.filters.SmallTest;
32import android.support.test.runner.AndroidJUnit4;
33import android.text.TextUtils;
34import android.view.inputmethod.CursorAnchorInfo.Builder;
35
36import org.junit.Test;
37import org.junit.runner.RunWith;
38
39import java.util.Objects;
40
41@SmallTest
42@RunWith(AndroidJUnit4.class)
43public class CursorAnchorInfoTest {
44    private static final float EPSILON = 0.0000001f;
45
46    private static final RectF[] MANY_BOUNDS = new RectF[] {
47            new RectF(101.0f, 201.0f, 301.0f, 401.0f),
48            new RectF(102.0f, 202.0f, 302.0f, 402.0f),
49            new RectF(103.0f, 203.0f, 303.0f, 403.0f),
50            new RectF(104.0f, 204.0f, 304.0f, 404.0f),
51            new RectF(105.0f, 205.0f, 305.0f, 405.0f),
52            new RectF(106.0f, 206.0f, 306.0f, 406.0f),
53            new RectF(107.0f, 207.0f, 307.0f, 407.0f),
54            new RectF(108.0f, 208.0f, 308.0f, 408.0f),
55            new RectF(109.0f, 209.0f, 309.0f, 409.0f),
56            new RectF(110.0f, 210.0f, 310.0f, 410.0f),
57            new RectF(111.0f, 211.0f, 311.0f, 411.0f),
58            new RectF(112.0f, 212.0f, 312.0f, 412.0f),
59            new RectF(113.0f, 213.0f, 313.0f, 413.0f),
60            new RectF(114.0f, 214.0f, 314.0f, 414.0f),
61            new RectF(115.0f, 215.0f, 315.0f, 415.0f),
62            new RectF(116.0f, 216.0f, 316.0f, 416.0f),
63            new RectF(117.0f, 217.0f, 317.0f, 417.0f),
64            new RectF(118.0f, 218.0f, 318.0f, 418.0f),
65            new RectF(119.0f, 219.0f, 319.0f, 419.0f),
66    };
67    private static final int[] MANY_FLAGS_ARRAY = new int[] {
68        FLAG_HAS_INVISIBLE_REGION,
69        FLAG_HAS_INVISIBLE_REGION | FLAG_HAS_VISIBLE_REGION,
70        FLAG_HAS_VISIBLE_REGION,
71        FLAG_HAS_VISIBLE_REGION,
72        FLAG_HAS_VISIBLE_REGION,
73        FLAG_HAS_VISIBLE_REGION,
74        FLAG_HAS_VISIBLE_REGION | FLAG_IS_RTL,
75        FLAG_HAS_INVISIBLE_REGION | FLAG_HAS_VISIBLE_REGION | FLAG_IS_RTL,
76        FLAG_HAS_INVISIBLE_REGION | FLAG_IS_RTL,
77        FLAG_HAS_VISIBLE_REGION | FLAG_IS_RTL,
78        FLAG_HAS_VISIBLE_REGION,
79        FLAG_HAS_VISIBLE_REGION | FLAG_IS_RTL,
80        FLAG_HAS_VISIBLE_REGION,
81        FLAG_HAS_VISIBLE_REGION | FLAG_IS_RTL,
82        FLAG_HAS_VISIBLE_REGION,
83        FLAG_HAS_VISIBLE_REGION | FLAG_IS_RTL,
84        FLAG_HAS_VISIBLE_REGION,
85        FLAG_HAS_INVISIBLE_REGION,
86        FLAG_HAS_INVISIBLE_REGION | FLAG_IS_RTL,
87    };
88
89    @Test
90    public void testBuilder() throws Exception {
91        final int SELECTION_START = 30;
92        final int SELECTION_END = 40;
93        final int COMPOSING_TEXT_START = 32;
94        final String COMPOSING_TEXT = "test";
95        final int INSERTION_MARKER_FLAGS =
96                FLAG_HAS_VISIBLE_REGION | FLAG_HAS_INVISIBLE_REGION | FLAG_IS_RTL;
97        final float INSERTION_MARKER_HORIZONTAL = 10.5f;
98        final float INSERTION_MARKER_TOP = 100.1f;
99        final float INSERTION_MARKER_BASELINE = 110.4f;
100        final float INSERTION_MARKER_BOTOM = 111.0f;
101
102        Matrix TRANSFORM_MATRIX = new Matrix(Matrix.IDENTITY_MATRIX);
103        TRANSFORM_MATRIX.setScale(10.0f, 20.0f);
104
105        final Builder builder = new Builder();
106        builder.setSelectionRange(SELECTION_START, SELECTION_END)
107                .setComposingText(COMPOSING_TEXT_START, COMPOSING_TEXT)
108                .setInsertionMarkerLocation(INSERTION_MARKER_HORIZONTAL, INSERTION_MARKER_TOP,
109                        INSERTION_MARKER_BASELINE, INSERTION_MARKER_BOTOM, INSERTION_MARKER_FLAGS)
110                .setMatrix(TRANSFORM_MATRIX);
111        for (int i = 0; i < MANY_BOUNDS.length; i++) {
112            final RectF bounds = MANY_BOUNDS[i];
113            final int flags = MANY_FLAGS_ARRAY[i];
114            builder.addCharacterBounds(i, bounds.left, bounds.top, bounds.right, bounds.bottom,
115                    flags);
116        }
117
118        final CursorAnchorInfo info = builder.build();
119        assertEquals(SELECTION_START, info.getSelectionStart());
120        assertEquals(SELECTION_END, info.getSelectionEnd());
121        assertEquals(COMPOSING_TEXT_START, info.getComposingTextStart());
122        assertTrue(TextUtils.equals(COMPOSING_TEXT, info.getComposingText()));
123        assertEquals(INSERTION_MARKER_FLAGS, info.getInsertionMarkerFlags());
124        assertEquals(INSERTION_MARKER_HORIZONTAL, info.getInsertionMarkerHorizontal(), EPSILON);
125        assertEquals(INSERTION_MARKER_TOP, info.getInsertionMarkerTop(), EPSILON);
126        assertEquals(INSERTION_MARKER_BASELINE, info.getInsertionMarkerBaseline(), EPSILON);
127        assertEquals(INSERTION_MARKER_BOTOM, info.getInsertionMarkerBottom(), EPSILON);
128        assertEquals(TRANSFORM_MATRIX, info.getMatrix());
129        for (int i = 0; i < MANY_BOUNDS.length; i++) {
130            final RectF expectedBounds = MANY_BOUNDS[i];
131            assertEquals(expectedBounds, info.getCharacterBounds(i));
132        }
133        assertNull(info.getCharacterBounds(-1));
134        assertNull(info.getCharacterBounds(MANY_BOUNDS.length + 1));
135        for (int i = 0; i < MANY_FLAGS_ARRAY.length; i++) {
136            final int expectedFlags = MANY_FLAGS_ARRAY[i];
137            assertEquals(expectedFlags, info.getCharacterBoundsFlags(i));
138        }
139        assertEquals(0, info.getCharacterBoundsFlags(-1));
140        assertEquals(0, info.getCharacterBoundsFlags(MANY_BOUNDS.length + 1));
141
142        // Make sure that the builder can reproduce the same object.
143        final CursorAnchorInfo info2 = builder.build();
144        assertEquals(SELECTION_START, info2.getSelectionStart());
145        assertEquals(SELECTION_END, info2.getSelectionEnd());
146        assertEquals(COMPOSING_TEXT_START, info2.getComposingTextStart());
147        assertTrue(TextUtils.equals(COMPOSING_TEXT, info2.getComposingText()));
148        assertEquals(INSERTION_MARKER_FLAGS, info2.getInsertionMarkerFlags());
149        assertEquals(INSERTION_MARKER_HORIZONTAL, info2.getInsertionMarkerHorizontal(), EPSILON);
150        assertEquals(INSERTION_MARKER_TOP, info2.getInsertionMarkerTop(), EPSILON);
151        assertEquals(INSERTION_MARKER_BASELINE, info2.getInsertionMarkerBaseline(), EPSILON);
152        assertEquals(INSERTION_MARKER_BOTOM, info2.getInsertionMarkerBottom(), EPSILON);
153        assertEquals(TRANSFORM_MATRIX, info2.getMatrix());
154        for (int i = 0; i < MANY_BOUNDS.length; i++) {
155            final RectF expectedBounds = MANY_BOUNDS[i];
156            assertEquals(expectedBounds, info2.getCharacterBounds(i));
157        }
158        assertNull(info2.getCharacterBounds(-1));
159        assertNull(info2.getCharacterBounds(MANY_BOUNDS.length + 1));
160        for (int i = 0; i < MANY_FLAGS_ARRAY.length; i++) {
161            final int expectedFlags = MANY_FLAGS_ARRAY[i];
162            assertEquals(expectedFlags, info2.getCharacterBoundsFlags(i));
163        }
164        assertEquals(0, info2.getCharacterBoundsFlags(-1));
165        assertEquals(0, info2.getCharacterBoundsFlags(MANY_BOUNDS.length + 1));
166        assertEquals(info, info2);
167        assertEquals(info.hashCode(), info2.hashCode());
168
169        // Make sure that object can be marshaled via {@link Parsel}.
170        final CursorAnchorInfo info3 = cloneViaParcel(info2);
171        assertEquals(SELECTION_START, info3.getSelectionStart());
172        assertEquals(SELECTION_END, info3.getSelectionEnd());
173        assertEquals(COMPOSING_TEXT_START, info3.getComposingTextStart());
174        assertTrue(TextUtils.equals(COMPOSING_TEXT, info3.getComposingText()));
175        assertEquals(INSERTION_MARKER_FLAGS, info3.getInsertionMarkerFlags());
176        assertEquals(INSERTION_MARKER_HORIZONTAL, info3.getInsertionMarkerHorizontal(), EPSILON);
177        assertEquals(INSERTION_MARKER_TOP, info3.getInsertionMarkerTop(), EPSILON);
178        assertEquals(INSERTION_MARKER_BASELINE, info3.getInsertionMarkerBaseline(), EPSILON);
179        assertEquals(INSERTION_MARKER_BOTOM, info3.getInsertionMarkerBottom(), EPSILON);
180        assertEquals(TRANSFORM_MATRIX, info3.getMatrix());
181        for (int i = 0; i < MANY_BOUNDS.length; i++) {
182            final RectF expectedBounds = MANY_BOUNDS[i];
183            assertEquals(expectedBounds, info3.getCharacterBounds(i));
184        }
185        assertNull(info3.getCharacterBounds(-1));
186        assertNull(info3.getCharacterBounds(MANY_BOUNDS.length + 1));
187        for (int i = 0; i < MANY_FLAGS_ARRAY.length; i++) {
188            final int expectedFlags = MANY_FLAGS_ARRAY[i];
189            assertEquals(expectedFlags, info3.getCharacterBoundsFlags(i));
190        }
191        assertEquals(0, info3.getCharacterBoundsFlags(-1));
192        assertEquals(0, info3.getCharacterBoundsFlags(MANY_BOUNDS.length + 1));
193        assertEquals(info.hashCode(), info3.hashCode());
194
195        builder.reset();
196        final CursorAnchorInfo uninitializedInfo = builder.build();
197        assertEquals(-1, uninitializedInfo.getSelectionStart());
198        assertEquals(-1, uninitializedInfo.getSelectionEnd());
199        assertEquals(-1, uninitializedInfo.getComposingTextStart());
200        assertNull(uninitializedInfo.getComposingText());
201        assertEquals(0, uninitializedInfo.getInsertionMarkerFlags());
202        assertEquals(Float.NaN, uninitializedInfo.getInsertionMarkerHorizontal(), EPSILON);
203        assertEquals(Float.NaN, uninitializedInfo.getInsertionMarkerTop(), EPSILON);
204        assertEquals(Float.NaN, uninitializedInfo.getInsertionMarkerBaseline(), EPSILON);
205        assertEquals(Float.NaN, uninitializedInfo.getInsertionMarkerBottom(), EPSILON);
206        assertEquals(Matrix.IDENTITY_MATRIX, uninitializedInfo.getMatrix());
207    }
208
209    private static void assertNotEquals(final CursorAnchorInfo reference,
210            final CursorAnchorInfo actual) {
211        assertFalse(Objects.equals(reference, actual));
212    }
213
214    @Test
215    public void testEquality() throws Exception {
216        final Matrix MATRIX1 = new Matrix();
217        MATRIX1.setTranslate(10.0f, 20.0f);
218        final Matrix MATRIX2 = new Matrix();
219        MATRIX2.setTranslate(110.0f, 120.0f);
220        final Matrix NAN_MATRIX = new Matrix();
221        NAN_MATRIX.setValues(new float[]{
222                Float.NaN, Float.NaN, Float.NaN,
223                Float.NaN, Float.NaN, Float.NaN,
224                Float.NaN, Float.NaN, Float.NaN});
225        final int SELECTION_START1 = 2;
226        final int SELECTION_END1 = 7;
227        final String COMPOSING_TEXT1 = "0123456789";
228        final int COMPOSING_TEXT_START1 = 0;
229        final int INSERTION_MARKER_FLAGS1 = FLAG_HAS_VISIBLE_REGION;
230        final float INSERTION_MARKER_HORIZONTAL1 = 10.5f;
231        final float INSERTION_MARKER_TOP1 = 100.1f;
232        final float INSERTION_MARKER_BASELINE1 = 110.4f;
233        final float INSERTION_MARKER_BOTOM1 = 111.0f;
234        final int SELECTION_START2 = 4;
235        final int SELECTION_END2 = 8;
236        final String COMPOSING_TEXT2 = "9876543210";
237        final int COMPOSING_TEXT_START2 = 3;
238        final int INSERTION_MARKER_FLAGS2 =
239                FLAG_HAS_VISIBLE_REGION | FLAG_HAS_INVISIBLE_REGION | FLAG_IS_RTL;
240        final float INSERTION_MARKER_HORIZONTAL2 = 14.5f;
241        final float INSERTION_MARKER_TOP2 = 200.1f;
242        final float INSERTION_MARKER_BASELINE2 = 210.4f;
243        final float INSERTION_MARKER_BOTOM2 = 211.0f;
244
245        // Default instance should be equal.
246        assertEquals(new Builder().build(), new Builder().build());
247
248        assertEquals(
249                new Builder().setSelectionRange(SELECTION_START1, SELECTION_END1).build(),
250                new Builder().setSelectionRange(SELECTION_START1, SELECTION_END1).build());
251        assertNotEquals(
252                new Builder().setSelectionRange(SELECTION_START1, SELECTION_END1).build(),
253                new Builder().setSelectionRange(SELECTION_START1, SELECTION_END2).build());
254        assertNotEquals(
255                new Builder().setSelectionRange(SELECTION_START1, SELECTION_END1).build(),
256                new Builder().setSelectionRange(SELECTION_START2, SELECTION_END1).build());
257        assertNotEquals(
258                new Builder().setSelectionRange(SELECTION_START1, SELECTION_END1).build(),
259                new Builder().setSelectionRange(SELECTION_START2, SELECTION_END2).build());
260        assertEquals(
261                new Builder().setComposingText(COMPOSING_TEXT_START1, COMPOSING_TEXT1).build(),
262                new Builder().setComposingText(COMPOSING_TEXT_START1, COMPOSING_TEXT1).build());
263        assertNotEquals(
264                new Builder().setComposingText(COMPOSING_TEXT_START1, COMPOSING_TEXT1).build(),
265                new Builder().setComposingText(COMPOSING_TEXT_START2, COMPOSING_TEXT1).build());
266        assertNotEquals(
267                new Builder().setComposingText(COMPOSING_TEXT_START1, COMPOSING_TEXT1).build(),
268                new Builder().setComposingText(COMPOSING_TEXT_START1, COMPOSING_TEXT2).build());
269        assertNotEquals(
270                new Builder().setComposingText(COMPOSING_TEXT_START1, COMPOSING_TEXT1).build(),
271                new Builder().setComposingText(COMPOSING_TEXT_START2, COMPOSING_TEXT2).build());
272
273        // For insertion marker locations, {@link Float#NaN} is treated as if it was a number.
274        assertEquals(
275                new Builder().setMatrix(MATRIX1).setInsertionMarkerLocation(
276                        Float.NaN, Float.NaN, Float.NaN, Float.NaN,
277                        INSERTION_MARKER_FLAGS1).build(),
278                new Builder().setMatrix(MATRIX1).setInsertionMarkerLocation(
279                        Float.NaN, Float.NaN, Float.NaN, Float.NaN,
280                        INSERTION_MARKER_FLAGS1).build());
281
282        // Check Matrix.
283        assertEquals(
284                new Builder().setMatrix(MATRIX1).build(),
285                new Builder().setMatrix(MATRIX1).build());
286        assertNotEquals(
287                new Builder().setMatrix(MATRIX1).build(),
288                new Builder().setMatrix(MATRIX2).build());
289        assertNotEquals(
290                new Builder().setMatrix(MATRIX1).build(),
291                new Builder().setMatrix(NAN_MATRIX).build());
292        // Unlike insertion marker locations, {@link Float#NaN} in the matrix is treated as just a
293        // NaN as usual (NaN == NaN -> false).
294        assertNotEquals(
295                new Builder().setMatrix(NAN_MATRIX).build(),
296                new Builder().setMatrix(NAN_MATRIX).build());
297
298        assertEquals(
299                new Builder().setMatrix(MATRIX1).setInsertionMarkerLocation(
300                        INSERTION_MARKER_HORIZONTAL1, INSERTION_MARKER_TOP1,
301                        INSERTION_MARKER_BASELINE1, INSERTION_MARKER_BOTOM1,
302                        INSERTION_MARKER_FLAGS1).build(),
303                new Builder().setMatrix(MATRIX1).setInsertionMarkerLocation(
304                        INSERTION_MARKER_HORIZONTAL1, INSERTION_MARKER_TOP1,
305                        INSERTION_MARKER_BASELINE1, INSERTION_MARKER_BOTOM1,
306                        INSERTION_MARKER_FLAGS1).build());
307        assertNotEquals(
308                new Builder().setMatrix(MATRIX1).setInsertionMarkerLocation(
309                        Float.NaN, INSERTION_MARKER_TOP1,
310                        INSERTION_MARKER_BASELINE1, INSERTION_MARKER_BOTOM1,
311                        INSERTION_MARKER_FLAGS1).build(),
312                new Builder().setMatrix(MATRIX1).setInsertionMarkerLocation(
313                        INSERTION_MARKER_HORIZONTAL1, INSERTION_MARKER_TOP1,
314                        INSERTION_MARKER_BASELINE1, INSERTION_MARKER_BOTOM1,
315                        INSERTION_MARKER_FLAGS1).build());
316        assertNotEquals(
317                new Builder().setMatrix(MATRIX1).setInsertionMarkerLocation(
318                        INSERTION_MARKER_HORIZONTAL1, INSERTION_MARKER_TOP1,
319                        INSERTION_MARKER_BASELINE1, INSERTION_MARKER_BOTOM1,
320                        INSERTION_MARKER_FLAGS1).build(),
321                new Builder().setMatrix(MATRIX1).setInsertionMarkerLocation(
322                        INSERTION_MARKER_HORIZONTAL2, INSERTION_MARKER_TOP1,
323                        INSERTION_MARKER_BASELINE1, INSERTION_MARKER_BOTOM1,
324                        INSERTION_MARKER_FLAGS1).build());
325        assertNotEquals(
326                new Builder().setMatrix(MATRIX1).setInsertionMarkerLocation(
327                        INSERTION_MARKER_HORIZONTAL1, INSERTION_MARKER_TOP1,
328                        INSERTION_MARKER_BASELINE1, INSERTION_MARKER_BOTOM1,
329                        INSERTION_MARKER_FLAGS1).build(),
330                new Builder().setMatrix(MATRIX1).setInsertionMarkerLocation(
331                        INSERTION_MARKER_HORIZONTAL1, INSERTION_MARKER_TOP2,
332                        INSERTION_MARKER_BASELINE1, INSERTION_MARKER_BOTOM1,
333                        INSERTION_MARKER_FLAGS1).build());
334        assertNotEquals(
335                new Builder().setMatrix(MATRIX1).setInsertionMarkerLocation(
336                        INSERTION_MARKER_HORIZONTAL1, INSERTION_MARKER_TOP1,
337                        INSERTION_MARKER_BASELINE1, INSERTION_MARKER_BOTOM1,
338                        INSERTION_MARKER_FLAGS1).build(),
339                new Builder().setMatrix(MATRIX1).setInsertionMarkerLocation(
340                        INSERTION_MARKER_HORIZONTAL1, INSERTION_MARKER_TOP1,
341                        INSERTION_MARKER_BASELINE2, INSERTION_MARKER_BOTOM1,
342                        INSERTION_MARKER_FLAGS1).build());
343        assertNotEquals(
344                new Builder().setMatrix(MATRIX1).setInsertionMarkerLocation(
345                        INSERTION_MARKER_HORIZONTAL1, INSERTION_MARKER_TOP1,
346                        INSERTION_MARKER_BASELINE1, INSERTION_MARKER_BOTOM1,
347                        INSERTION_MARKER_FLAGS1).build(),
348                new Builder().setMatrix(MATRIX1).setInsertionMarkerLocation(
349                        INSERTION_MARKER_HORIZONTAL2, INSERTION_MARKER_TOP1,
350                        INSERTION_MARKER_BASELINE1, INSERTION_MARKER_BOTOM1,
351                        INSERTION_MARKER_FLAGS1).build());
352        assertNotEquals(
353                new Builder().setMatrix(MATRIX1).setInsertionMarkerLocation(
354                        INSERTION_MARKER_HORIZONTAL1, INSERTION_MARKER_TOP1,
355                        INSERTION_MARKER_BASELINE1, INSERTION_MARKER_BOTOM1,
356                        INSERTION_MARKER_FLAGS1).build(),
357                new Builder().setMatrix(MATRIX1).setInsertionMarkerLocation(
358                        INSERTION_MARKER_HORIZONTAL1, INSERTION_MARKER_TOP1,
359                        INSERTION_MARKER_BASELINE1, INSERTION_MARKER_BOTOM2,
360                        INSERTION_MARKER_FLAGS1).build());
361        assertNotEquals(
362                new Builder().setMatrix(MATRIX1).setInsertionMarkerLocation(
363                        INSERTION_MARKER_HORIZONTAL1, INSERTION_MARKER_TOP1,
364                        INSERTION_MARKER_BASELINE1, INSERTION_MARKER_BOTOM1,
365                        INSERTION_MARKER_FLAGS1).build(),
366                new Builder().setMatrix(MATRIX1).setInsertionMarkerLocation(
367                        INSERTION_MARKER_HORIZONTAL1, INSERTION_MARKER_TOP1,
368                        INSERTION_MARKER_BASELINE1, INSERTION_MARKER_BOTOM1,
369                        INSERTION_MARKER_FLAGS2).build());
370    }
371
372    @Test
373    public void testMatrixIsCopied() throws Exception {
374        final Matrix MATRIX1 = new Matrix();
375        MATRIX1.setTranslate(10.0f, 20.0f);
376        final Matrix MATRIX2 = new Matrix();
377        MATRIX2.setTranslate(110.0f, 120.0f);
378        final Matrix MATRIX3 = new Matrix();
379        MATRIX3.setTranslate(210.0f, 220.0f);
380        final Matrix matrix = new Matrix();
381        final Builder builder = new Builder();
382
383        matrix.set(MATRIX1);
384        builder.setMatrix(matrix);
385        matrix.postRotate(90.0f);
386
387        final CursorAnchorInfo firstInstance = builder.build();
388        assertEquals(MATRIX1, firstInstance.getMatrix());
389        matrix.set(MATRIX2);
390        builder.setMatrix(matrix);
391        final CursorAnchorInfo secondInstance = builder.build();
392        assertEquals(MATRIX1, firstInstance.getMatrix());
393        assertEquals(MATRIX2, secondInstance.getMatrix());
394
395        matrix.set(MATRIX3);
396        assertEquals(MATRIX1, firstInstance.getMatrix());
397        assertEquals(MATRIX2, secondInstance.getMatrix());
398    }
399
400    @Test
401    public void testMatrixIsRequired() throws Exception {
402        final int SELECTION_START = 30;
403        final int SELECTION_END = 40;
404        final int COMPOSING_TEXT_START = 32;
405        final String COMPOSING_TEXT = "test";
406        final int INSERTION_MARKER_FLAGS = FLAG_HAS_VISIBLE_REGION;
407        final float INSERTION_MARKER_HORIZONTAL = 10.5f;
408        final float INSERTION_MARKER_TOP = 100.1f;
409        final float INSERTION_MARKER_BASELINE = 110.4f;
410        final float INSERTION_MARKER_BOTOM = 111.0f;
411        Matrix TRANSFORM_MATRIX = new Matrix(Matrix.IDENTITY_MATRIX);
412        TRANSFORM_MATRIX.setScale(10.0f, 20.0f);
413
414        final Builder builder = new Builder();
415        // Check twice to make sure if Builder#reset() works as expected.
416        for (int repeatCount = 0; repeatCount < 2; ++repeatCount) {
417            builder.setSelectionRange(SELECTION_START, SELECTION_END)
418                    .setComposingText(COMPOSING_TEXT_START, COMPOSING_TEXT);
419            try {
420                // Should succeed as coordinate transformation matrix is not required if no
421                // positional information is specified.
422                builder.build();
423            } catch (IllegalArgumentException ex) {
424                assertTrue(false);
425            }
426
427            builder.setInsertionMarkerLocation(INSERTION_MARKER_HORIZONTAL, INSERTION_MARKER_TOP,
428                    INSERTION_MARKER_BASELINE, INSERTION_MARKER_BOTOM, INSERTION_MARKER_FLAGS);
429            try {
430                // Coordinate transformation matrix is required if no positional information is
431                // specified.
432                builder.build();
433                assertTrue(false);
434            } catch (IllegalArgumentException ex) {
435            }
436
437            builder.setMatrix(TRANSFORM_MATRIX);
438            try {
439                // Should succeed as coordinate transformation matrix is required.
440                builder.build();
441            } catch (IllegalArgumentException ex) {
442                assertTrue(false);
443            }
444
445            builder.reset();
446        }
447    }
448
449    @Test
450    public void testBuilderAddCharacterBounds() throws Exception {
451        // A negative index should be rejected.
452        try {
453            new Builder().addCharacterBounds(-1, 0.0f, 0.0f, 0.0f, 0.0f, FLAG_HAS_VISIBLE_REGION);
454            assertTrue(false);
455        } catch (IllegalArgumentException ex) {
456        }
457    }
458
459    private static CursorAnchorInfo cloneViaParcel(final CursorAnchorInfo src) {
460        Parcel parcel = null;
461        try {
462            parcel = Parcel.obtain();
463            src.writeToParcel(parcel, 0);
464            parcel.setDataPosition(0);
465            return new CursorAnchorInfo(parcel);
466        } finally {
467            if (parcel != null) {
468                parcel.recycle();
469            }
470        }
471    }
472}
473