1 /* 2 * Copyright (C) 2008 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.text.cts; 18 19 import static org.junit.Assert.assertEquals; 20 import static org.junit.Assert.assertFalse; 21 import static org.junit.Assert.assertNull; 22 import static org.junit.Assert.assertSame; 23 import static org.junit.Assert.assertTrue; 24 import static org.junit.Assert.fail; 25 26 import android.graphics.Bitmap; 27 import android.graphics.Canvas; 28 import android.graphics.Paint; 29 import android.graphics.Path; 30 import android.graphics.Rect; 31 import android.graphics.RectF; 32 import android.support.test.filters.SmallTest; 33 import android.support.test.runner.AndroidJUnit4; 34 import android.text.Layout; 35 import android.text.Layout.Alignment; 36 import android.text.Spannable; 37 import android.text.SpannableString; 38 import android.text.TextPaint; 39 import android.text.style.StrikethroughSpan; 40 41 import org.junit.Before; 42 import org.junit.Test; 43 import org.junit.runner.RunWith; 44 45 import java.util.ArrayList; 46 import java.util.List; 47 48 @SmallTest 49 @RunWith(AndroidJUnit4.class) 50 public class LayoutTest { 51 private final static int LINE_COUNT = 5; 52 private final static int LINE_HEIGHT = 12; 53 private final static int LINE_DESCENT = 4; 54 private final static CharSequence LAYOUT_TEXT = "alwei\t;sdfs\ndf @"; 55 56 private int mWidth; 57 private Layout.Alignment mAlign; 58 private float mSpacingMult; 59 private float mSpacingAdd; 60 private SpannableString mSpannedText; 61 62 private TextPaint mTextPaint; 63 64 @Before setup()65 public void setup() { 66 mTextPaint = new TextPaint(); 67 mSpannedText = new SpannableString(LAYOUT_TEXT); 68 mSpannedText.setSpan(new StrikethroughSpan(), 0, 1, Spannable.SPAN_INCLUSIVE_EXCLUSIVE); 69 mWidth = 11; 70 mAlign = Alignment.ALIGN_CENTER; 71 mSpacingMult = 1; 72 mSpacingAdd = 2; 73 } 74 75 @Test testConstructor()76 public void testConstructor() { 77 new MockLayout(LAYOUT_TEXT, mTextPaint, mWidth, mAlign, mSpacingMult, mSpacingAdd); 78 } 79 80 @Test(expected=IllegalArgumentException.class) testConstructorNull()81 public void testConstructorNull() { 82 new MockLayout(null, null, -1, null, 0, 0); 83 } 84 85 @Test testGetText()86 public void testGetText() { 87 CharSequence text = "test case 1"; 88 Layout layout = new MockLayout(text, mTextPaint, mWidth, 89 mAlign, mSpacingMult, mSpacingAdd); 90 assertEquals(text, layout.getText()); 91 92 layout = new MockLayout(null, mTextPaint, mWidth, mAlign, mSpacingMult, mSpacingAdd); 93 assertNull(layout.getText()); 94 } 95 96 @Test testGetPaint()97 public void testGetPaint() { 98 Layout layout = new MockLayout(LAYOUT_TEXT, mTextPaint, mWidth, 99 mAlign, mSpacingMult, mSpacingAdd); 100 101 assertSame(mTextPaint, layout.getPaint()); 102 103 layout = new MockLayout(LAYOUT_TEXT, null, mWidth, mAlign, mSpacingMult, mSpacingAdd); 104 assertNull(layout.getPaint()); 105 } 106 107 @Test testGetWidth()108 public void testGetWidth() { 109 Layout layout = new MockLayout(LAYOUT_TEXT, mTextPaint, 10, 110 mAlign, mSpacingMult, mSpacingAdd); 111 assertEquals(10, layout.getWidth()); 112 113 layout = new MockLayout(LAYOUT_TEXT, mTextPaint, 0, mAlign, mSpacingMult, mSpacingAdd); 114 assertEquals(0, layout.getWidth()); 115 } 116 117 @Test testGetEllipsizedWidth()118 public void testGetEllipsizedWidth() { 119 Layout layout = new MockLayout(LAYOUT_TEXT, mTextPaint, 15, 120 mAlign, mSpacingMult, mSpacingAdd); 121 assertEquals(15, layout.getEllipsizedWidth()); 122 123 layout = new MockLayout(LAYOUT_TEXT, mTextPaint, 0, mAlign, mSpacingMult, mSpacingAdd); 124 assertEquals(0, layout.getEllipsizedWidth()); 125 } 126 127 @Test testIncreaseWidthTo()128 public void testIncreaseWidthTo() { 129 Layout layout = new MockLayout(LAYOUT_TEXT, mTextPaint, mWidth, 130 mAlign, mSpacingMult, mSpacingAdd); 131 int oldWidth = layout.getWidth(); 132 133 layout.increaseWidthTo(oldWidth); 134 assertEquals(oldWidth, layout.getWidth()); 135 136 try { 137 layout.increaseWidthTo(oldWidth - 1); 138 fail("should throw runtime exception attempted to reduce Layout width"); 139 } catch (RuntimeException e) { 140 } 141 142 layout.increaseWidthTo(oldWidth + 1); 143 assertEquals(oldWidth + 1, layout.getWidth()); 144 } 145 146 @Test testGetHeight()147 public void testGetHeight() { 148 Layout layout = new MockLayout(LAYOUT_TEXT, mTextPaint, mWidth, 149 mAlign, mSpacingMult, mSpacingAdd); 150 assertEquals(60, layout.getHeight()); 151 } 152 153 @Test testGetAlignment()154 public void testGetAlignment() { 155 Layout layout = new MockLayout(LAYOUT_TEXT, mTextPaint, mWidth, 156 mAlign, mSpacingMult, mSpacingAdd); 157 assertSame(mAlign, layout.getAlignment()); 158 159 layout = new MockLayout(LAYOUT_TEXT, mTextPaint, mWidth, null, mSpacingMult, mSpacingAdd); 160 assertNull(layout.getAlignment()); 161 } 162 163 @Test testGetSpacingMultiplier()164 public void testGetSpacingMultiplier() { 165 Layout layout = new MockLayout(LAYOUT_TEXT, mTextPaint, mWidth, mAlign, -1, mSpacingAdd); 166 assertEquals(-1.0f, layout.getSpacingMultiplier(), 0.0f); 167 168 layout = new MockLayout(LAYOUT_TEXT, mTextPaint, mWidth, mAlign, 5, mSpacingAdd); 169 assertEquals(5.0f, layout.getSpacingMultiplier(), 0.0f); 170 } 171 172 @Test testGetSpacingAdd()173 public void testGetSpacingAdd() { 174 Layout layout = new MockLayout(LAYOUT_TEXT, mTextPaint, mWidth, mAlign, mSpacingMult, -1); 175 assertEquals(-1.0f, layout.getSpacingAdd(), 0.0f); 176 177 layout = new MockLayout(LAYOUT_TEXT, mTextPaint, mWidth, mAlign, mSpacingMult, 20); 178 assertEquals(20.0f, layout.getSpacingAdd(), 0.0f); 179 } 180 181 @Test testGetLineBounds()182 public void testGetLineBounds() { 183 Layout layout = new MockLayout(LAYOUT_TEXT, mTextPaint, mWidth, 184 mAlign, mSpacingMult, mSpacingAdd); 185 Rect bounds = new Rect(); 186 187 assertEquals(32, layout.getLineBounds(2, bounds)); 188 assertEquals(0, bounds.left); 189 assertEquals(mWidth, bounds.right); 190 assertEquals(24, bounds.top); 191 assertEquals(36, bounds.bottom); 192 } 193 194 @Test testGetLineForVertical()195 public void testGetLineForVertical() { 196 Layout layout = new MockLayout(LAYOUT_TEXT, mTextPaint, mWidth, 197 mAlign, mSpacingMult, mSpacingAdd); 198 assertEquals(0, layout.getLineForVertical(-1)); 199 assertEquals(0, layout.getLineForVertical(0)); 200 assertEquals(0, layout.getLineForVertical(LINE_COUNT)); 201 assertEquals(LINE_COUNT - 1, layout.getLineForVertical(1000)); 202 } 203 204 @Test testGetLineForOffset()205 public void testGetLineForOffset() { 206 Layout layout = new MockLayout(LAYOUT_TEXT, mTextPaint, mWidth, 207 mAlign, mSpacingMult, mSpacingAdd); 208 assertEquals(0, layout.getLineForOffset(-1)); 209 assertEquals(1, layout.getLineForOffset(1)); 210 assertEquals(LINE_COUNT - 1, layout.getLineForOffset(LINE_COUNT - 1)); 211 assertEquals(LINE_COUNT - 1, layout.getLineForOffset(1000)); 212 } 213 214 @Test testGetLineEnd()215 public void testGetLineEnd() { 216 Layout layout = new MockLayout(LAYOUT_TEXT, mTextPaint, mWidth, 217 mAlign, mSpacingMult, mSpacingAdd); 218 assertEquals(2, layout.getLineEnd(1)); 219 } 220 221 @Test testGetLineVisibleEnd()222 public void testGetLineVisibleEnd() { 223 Layout layout = new MockLayout(LAYOUT_TEXT, mTextPaint, mWidth, 224 mAlign, mSpacingMult, mSpacingAdd); 225 226 assertEquals(2, layout.getLineVisibleEnd(1)); 227 assertEquals(LINE_COUNT, layout.getLineVisibleEnd(LINE_COUNT - 1)); 228 assertEquals(LAYOUT_TEXT.length(), layout.getLineVisibleEnd(LAYOUT_TEXT.length() - 1)); 229 try { 230 layout.getLineVisibleEnd(LAYOUT_TEXT.length()); 231 fail("should throw .StringIndexOutOfBoundsException here"); 232 } catch (StringIndexOutOfBoundsException e) { 233 } 234 } 235 236 @Test testGetLineBottom()237 public void testGetLineBottom() { 238 Layout layout = new MockLayout(LAYOUT_TEXT, mTextPaint, mWidth, 239 mAlign, mSpacingMult, mSpacingAdd); 240 assertEquals(LINE_HEIGHT, layout.getLineBottom(0)); 241 } 242 243 @Test testGetLineBaseline()244 public void testGetLineBaseline() { 245 Layout layout = new MockLayout(LAYOUT_TEXT, mTextPaint, mWidth, 246 mAlign, mSpacingMult, mSpacingAdd); 247 assertEquals(8, layout.getLineBaseline(0)); 248 } 249 250 @Test testGetLineAscent()251 public void testGetLineAscent() { 252 Layout layout = new MockLayout(LAYOUT_TEXT, mTextPaint, mWidth, 253 mAlign, mSpacingMult, mSpacingAdd); 254 assertEquals(-8, layout.getLineAscent(0)); 255 } 256 257 @Test testGetParagraphAlignment()258 public void testGetParagraphAlignment() { 259 Layout layout = new MockLayout(LAYOUT_TEXT, mTextPaint, mWidth, 260 mAlign, mSpacingMult, mSpacingAdd); 261 assertSame(mAlign, layout.getParagraphAlignment(0)); 262 263 layout = new MockLayout(mSpannedText, mTextPaint, mWidth, 264 mAlign, mSpacingMult, mSpacingAdd); 265 assertSame(mAlign, layout.getParagraphAlignment(0)); 266 assertSame(mAlign, layout.getParagraphAlignment(1)); 267 } 268 269 @Test testGetParagraphLeft()270 public void testGetParagraphLeft() { 271 Layout layout = new MockLayout(LAYOUT_TEXT, mTextPaint, mWidth, 272 mAlign, mSpacingMult, mSpacingAdd); 273 assertEquals(0, layout.getParagraphLeft(0)); 274 } 275 276 @Test testGetParagraphRight()277 public void testGetParagraphRight() { 278 Layout layout = new MockLayout(LAYOUT_TEXT, mTextPaint, mWidth, 279 mAlign, mSpacingMult, mSpacingAdd); 280 assertEquals(mWidth, layout.getParagraphRight(0)); 281 } 282 283 @Test testIsSpanned()284 public void testIsSpanned() { 285 MockLayout layout = new MockLayout(LAYOUT_TEXT, mTextPaint, mWidth, 286 mAlign, mSpacingMult, mSpacingAdd); 287 // default is not spanned text 288 assertFalse(layout.mockIsSpanned()); 289 290 // try to create a spanned text 291 layout = new MockLayout(mSpannedText, mTextPaint, mWidth, 292 mAlign, mSpacingMult, mSpacingAdd); 293 assertTrue(layout.mockIsSpanned()); 294 } 295 296 @Test testGetDesiredWidthRange()297 public void testGetDesiredWidthRange() { 298 CharSequence textShort = "test"; 299 CharSequence textLonger = "test\ngetDesiredWidth"; 300 CharSequence textLongest = "test getDesiredWidth"; 301 TextPaint paint = new TextPaint(); 302 float widthShort = Layout.getDesiredWidth(textShort, 0, textShort.length(), paint); 303 float widthLonger = Layout.getDesiredWidth(textLonger, 0, textLonger.length(), paint); 304 float widthLongest = Layout.getDesiredWidth(textLongest, 0, textLongest.length(), paint); 305 float widthPartShort = Layout.getDesiredWidth(textShort, 2, textShort.length(), paint); 306 float widthZero = Layout.getDesiredWidth(textLonger, 5, textShort.length() - 3, paint); 307 assertTrue(widthLonger > widthShort); 308 assertTrue(widthLongest > widthLonger); 309 assertEquals(0f, widthZero, 0.0f); 310 assertTrue(widthShort > widthPartShort); 311 } 312 313 @Test testGetDesiredWidth()314 public void testGetDesiredWidth() { 315 CharSequence textShort = "test"; 316 CharSequence textLonger = "test\ngetDesiredWidth"; 317 CharSequence textLongest = "test getDesiredWidth"; 318 TextPaint paint = new TextPaint(); 319 float widthShort = Layout.getDesiredWidth(textShort, paint); 320 float widthLonger = Layout.getDesiredWidth(textLonger, paint); 321 float widthLongest = Layout.getDesiredWidth(textLongest, paint); 322 assertTrue(widthLonger > widthShort); 323 assertTrue(widthLongest > widthLonger); 324 } 325 326 private static final class MockLayout extends Layout { MockLayout(CharSequence text, TextPaint paint, int width, Alignment align, float spacingmult, float spacingadd)327 public MockLayout(CharSequence text, TextPaint paint, int width, 328 Alignment align, float spacingmult, float spacingadd) { 329 super(text, paint, width, align, spacingmult, spacingadd); 330 } 331 mockIsSpanned()332 protected boolean mockIsSpanned() { 333 return super.isSpanned(); 334 } 335 336 @Override getBottomPadding()337 public int getBottomPadding() { 338 return 0; 339 } 340 341 @Override getEllipsisCount(int line)342 public int getEllipsisCount(int line) { 343 return 0; 344 } 345 346 @Override getEllipsisStart(int line)347 public int getEllipsisStart(int line) { 348 return 0; 349 } 350 351 @Override getLineContainsTab(int line)352 public boolean getLineContainsTab(int line) { 353 return false; 354 } 355 356 @Override getLineCount()357 public int getLineCount() { 358 return LINE_COUNT; 359 } 360 361 @Override getLineDescent(int line)362 public int getLineDescent(int line) { 363 return LINE_DESCENT; 364 } 365 366 @Override getLineDirections(int line)367 public Directions getLineDirections(int line) { 368 return Layout.DIRS_ALL_LEFT_TO_RIGHT; 369 } 370 371 @Override getLineStart(int line)372 public int getLineStart(int line) { 373 if (line < 0) { 374 return 0; 375 } 376 return line; 377 } 378 379 @Override getLineTop(int line)380 public int getLineTop(int line) { 381 if (line < 0) { 382 return 0; 383 } 384 return LINE_HEIGHT * (line); 385 } 386 387 @Override getParagraphDirection(int line)388 public int getParagraphDirection(int line) { 389 return 0; 390 } 391 392 @Override getTopPadding()393 public int getTopPadding() { 394 return 0; 395 } 396 } 397 398 @Test testGetLineWidth()399 public void testGetLineWidth() { 400 Layout layout = new MockLayout(LAYOUT_TEXT, mTextPaint, mWidth, 401 mAlign, mSpacingMult, mSpacingAdd); 402 for (int i = 0; i < LINE_COUNT; i++) { 403 int start = layout.getLineStart(i); 404 int end = layout.getLineEnd(i); 405 String text = LAYOUT_TEXT.toString().substring(start, end); 406 assertEquals(mTextPaint.measureText(text), layout.getLineWidth(i), 1.0f); 407 } 408 } 409 410 @Test testGetCursorPath()411 public void testGetCursorPath() { 412 Layout layout = new MockLayout(LAYOUT_TEXT, mTextPaint, mWidth, 413 mAlign, mSpacingMult, mSpacingAdd); 414 Path path = new Path(); 415 final float epsilon = 1.0f; 416 for (int i = 0; i < LINE_COUNT; i++) { 417 layout.getCursorPath(i, path, LAYOUT_TEXT); 418 RectF bounds = new RectF(); 419 path.computeBounds(bounds, false); 420 assertTrue(bounds.top >= layout.getLineTop(i) - epsilon); 421 assertTrue(bounds.bottom <= layout.getLineBottom(i) + epsilon); 422 } 423 } 424 425 @Test testDraw()426 public void testDraw() { 427 Layout layout = new MockLayout(LAYOUT_TEXT, mTextPaint, mWidth, 428 mAlign, mSpacingMult, mSpacingAdd); 429 final int width = 256; 430 final int height = 256; 431 MockCanvas c = new MockCanvas(width, height); 432 layout.draw(c); 433 List<MockCanvas.DrawCommand> drawCommands = c.getDrawCommands(); 434 assertEquals(LINE_COUNT, drawCommands.size()); 435 for (int i = 0; i < LINE_COUNT; i++) { 436 MockCanvas.DrawCommand drawCommand = drawCommands.get(i); 437 int start = layout.getLineStart(i); 438 int end = layout.getLineEnd(i); 439 assertEquals(LAYOUT_TEXT.toString().substring(start, end), drawCommand.text); 440 float expected_y = (i + 1) * LINE_HEIGHT - LINE_DESCENT; 441 assertEquals(expected_y, drawCommand.y, 0.0f); 442 } 443 } 444 445 private final class MockCanvas extends Canvas { 446 447 class DrawCommand { 448 final String text; 449 final float x; 450 final float y; 451 DrawCommand(String text, float x, float y)452 DrawCommand(String text, float x, float y) { 453 this.text = text; 454 this.x = x; 455 this.y = y; 456 } 457 } 458 459 List<DrawCommand> mDrawCommands; 460 MockCanvas(int width, int height)461 public MockCanvas(int width, int height) { 462 super(); 463 mDrawCommands = new ArrayList<>(); 464 Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); 465 setBitmap(bitmap); 466 } 467 468 // Drawing text with either drawText or drawTextRun is valid; we don't care which. 469 // We also don't care which of the string representations is used. 470 471 @Override drawText(String text, int start, int end, float x, float y, Paint p)472 public void drawText(String text, int start, int end, float x, float y, Paint p) { 473 mDrawCommands.add(new DrawCommand(text.substring(start, end), x, y)); 474 } 475 476 @Override drawText(CharSequence text, int start, int end, float x, float y, Paint p)477 public void drawText(CharSequence text, int start, int end, float x, float y, Paint p) { 478 drawText(text.toString(), start, end, x, y, p); 479 } 480 481 @Override drawText(char[] text, int index, int count, float x, float y, Paint p)482 public void drawText(char[] text, int index, int count, float x, float y, Paint p) { 483 mDrawCommands.add(new DrawCommand(new String(text, index, count), x, y)); 484 } 485 486 @Override drawTextRun(CharSequence text, int start, int end, int contextStart, int contextEnd, float x, float y, boolean isRtl, Paint paint)487 public void drawTextRun(CharSequence text, int start, int end, int contextStart, 488 int contextEnd, float x, float y, boolean isRtl, Paint paint) { 489 drawText(text, start, end, x, y, paint); 490 } 491 492 @Override drawTextRun(char[] text, int index, int count, int contextIndex, int contextCount, float x, float y, boolean isRtl, Paint paint)493 public void drawTextRun(char[] text, int index, int count, int contextIndex, 494 int contextCount, float x, float y, boolean isRtl, Paint paint) { 495 drawText(text, index, count, x, y, paint); 496 } 497 getDrawCommands()498 List<DrawCommand> getDrawCommands() { 499 return mDrawCommands; 500 } 501 } 502 } 503