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 17package android.text; 18 19import static org.junit.Assert.assertEquals; 20import static org.junit.Assert.assertFalse; 21import static org.junit.Assert.assertNull; 22import static org.junit.Assert.assertSame; 23import static org.junit.Assert.assertTrue; 24import static org.junit.Assert.fail; 25 26import android.graphics.Bitmap; 27import android.graphics.Canvas; 28import android.graphics.Paint; 29import android.graphics.Path; 30import android.graphics.Rect; 31import android.graphics.RectF; 32import android.support.test.filters.SmallTest; 33import android.support.test.runner.AndroidJUnit4; 34import android.text.Layout.Alignment; 35import android.text.style.StrikethroughSpan; 36 37import org.junit.Before; 38import org.junit.Test; 39import org.junit.runner.RunWith; 40 41import java.util.ArrayList; 42import java.util.List; 43 44@SmallTest 45@RunWith(AndroidJUnit4.class) 46public 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 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 72 public void testConstructor() { 73 new MockLayout(LAYOUT_TEXT, mTextPaint, mWidth, mAlign, mSpacingMult, mSpacingAdd); 74 } 75 76 @Test(expected = IllegalArgumentException.class) 77 public void testConstructorNull() { 78 new MockLayout(null, null, -1, null, 0, 0); 79 } 80 81 @Test 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 { 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 298 protected boolean mockIsSpanned() { 299 return super.isSpanned(); 300 } 301 302 @Override 303 public int getBottomPadding() { 304 return 0; 305 } 306 307 @Override 308 public int getEllipsisCount(int line) { 309 return 0; 310 } 311 312 @Override 313 public int getEllipsisStart(int line) { 314 return 0; 315 } 316 317 @Override 318 public boolean getLineContainsTab(int line) { 319 return false; 320 } 321 322 @Override 323 public int getLineCount() { 324 return LINE_COUNT; 325 } 326 327 @Override 328 public int getLineDescent(int line) { 329 return LINE_DESCENT; 330 } 331 332 @Override 333 public Directions getLineDirections(int line) { 334 return Layout.DIRS_ALL_LEFT_TO_RIGHT; 335 } 336 337 @Override 338 public int getLineStart(int line) { 339 if (line < 0) { 340 return 0; 341 } 342 return line; 343 } 344 345 @Override 346 public int getLineTop(int line) { 347 if (line < 0) { 348 return 0; 349 } 350 return LINE_HEIGHT * (line); 351 } 352 353 @Override 354 public int getParagraphDirection(int line) { 355 return 0; 356 } 357 358 @Override 359 public int getTopPadding() { 360 return 0; 361 } 362 } 363 364 @Test 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 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 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 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 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 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 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 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 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 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 464 List<DrawCommand> getDrawCommands() { 465 return mDrawCommands; 466 } 467 } 468} 469 470