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