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