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