Home | History | Annotate | Download | only in text
      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.platform.test.annotations.Presubmit;
     33 import android.support.test.filters.SmallTest;
     34 import android.support.test.runner.AndroidJUnit4;
     35 import android.text.Layout.Alignment;
     36 import android.text.style.StrikethroughSpan;
     37 
     38 import org.junit.Before;
     39 import org.junit.Test;
     40 import org.junit.runner.RunWith;
     41 
     42 import java.util.ArrayList;
     43 import java.util.List;
     44 import java.util.Locale;
     45 
     46 @Presubmit
     47 @SmallTest
     48 @RunWith(AndroidJUnit4.class)
     49 public class LayoutTest {
     50     private static final int LINE_COUNT = 5;
     51     private static final int LINE_HEIGHT = 12;
     52     private static final int LINE_DESCENT = 4;
     53     private static final CharSequence LAYOUT_TEXT = "alwei\t;sdfs\ndf @";
     54 
     55     private SpannableString mSpannedText;
     56 
     57     private int mWidth;
     58     private Layout.Alignment mAlign;
     59     private float mSpacingMult;
     60     private float mSpacingAdd;
     61     private TextPaint mTextPaint;
     62 
     63     @Before
     64     public void setup() {
     65         mTextPaint = new TextPaint();
     66         mSpannedText = new SpannableString(LAYOUT_TEXT);
     67         mSpannedText.setSpan(new StrikethroughSpan(), 0, 1, Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
     68         mWidth = 11;
     69         mAlign = Alignment.ALIGN_CENTER;
     70         mSpacingMult = 1;
     71         mSpacingAdd = 2;
     72     }
     73 
     74     @Test
     75     public void testConstructor() {
     76         new MockLayout(LAYOUT_TEXT, mTextPaint, mWidth, mAlign, mSpacingMult, mSpacingAdd);
     77     }
     78 
     79     @Test(expected = IllegalArgumentException.class)
     80     public void testConstructorNull() {
     81         new MockLayout(null, null, -1, null, 0, 0);
     82     }
     83 
     84     @Test
     85     public void testGetText() {
     86         CharSequence text = "test case 1";
     87         Layout layout = new MockLayout(text, mTextPaint, mWidth,
     88                 mAlign, mSpacingMult, mSpacingAdd);
     89         assertEquals(text, layout.getText());
     90 
     91         layout = new MockLayout(null, mTextPaint, mWidth, mAlign, mSpacingMult, mSpacingAdd);
     92         assertNull(layout.getText());
     93     }
     94 
     95     @Test
     96     public void testGetPaint() {
     97         Layout layout = new MockLayout(LAYOUT_TEXT, mTextPaint, mWidth,
     98                 mAlign, mSpacingMult, mSpacingAdd);
     99 
    100         assertSame(mTextPaint, layout.getPaint());
    101 
    102         layout = new MockLayout(LAYOUT_TEXT, null, mWidth, mAlign, mSpacingMult, mSpacingAdd);
    103         assertNull(layout.getPaint());
    104     }
    105 
    106     @Test
    107     public void testGetWidth() {
    108         Layout layout = new MockLayout(LAYOUT_TEXT, mTextPaint, 10,
    109                 mAlign, mSpacingMult, mSpacingAdd);
    110         assertEquals(10,  layout.getWidth());
    111 
    112         layout = new MockLayout(LAYOUT_TEXT, mTextPaint, 0, mAlign, mSpacingMult, mSpacingAdd);
    113         assertEquals(0,  layout.getWidth());
    114     }
    115 
    116     @Test
    117     public void testGetEllipsizedWidth() {
    118         Layout layout = new MockLayout(LAYOUT_TEXT, mTextPaint, 15,
    119                 mAlign, mSpacingMult, mSpacingAdd);
    120         assertEquals(15, layout.getEllipsizedWidth());
    121 
    122         layout = new MockLayout(LAYOUT_TEXT, mTextPaint, 0, mAlign, mSpacingMult, mSpacingAdd);
    123         assertEquals(0,  layout.getEllipsizedWidth());
    124     }
    125 
    126     @Test
    127     public void testIncreaseWidthTo() {
    128         Layout layout = new MockLayout(LAYOUT_TEXT, mTextPaint, mWidth,
    129                 mAlign, mSpacingMult, mSpacingAdd);
    130         int oldWidth = layout.getWidth();
    131 
    132         layout.increaseWidthTo(oldWidth);
    133         assertEquals(oldWidth, layout.getWidth());
    134 
    135         try {
    136             layout.increaseWidthTo(oldWidth - 1);
    137             fail("should throw runtime exception attempted to reduce Layout width");
    138         } catch (RuntimeException e) {
    139         }
    140 
    141         layout.increaseWidthTo(oldWidth + 1);
    142         assertEquals(oldWidth + 1, layout.getWidth());
    143     }
    144 
    145     @Test
    146     public void testGetHeight() {
    147         Layout layout = new MockLayout(LAYOUT_TEXT, mTextPaint, mWidth,
    148                 mAlign, mSpacingMult, mSpacingAdd);
    149         assertEquals(60, layout.getHeight());
    150     }
    151 
    152     @Test
    153     public void testGetAlignment() {
    154         Layout layout = new MockLayout(LAYOUT_TEXT, mTextPaint, mWidth,
    155                 mAlign, mSpacingMult, mSpacingAdd);
    156         assertSame(mAlign, layout.getAlignment());
    157 
    158         layout = new MockLayout(LAYOUT_TEXT, mTextPaint, mWidth, null, mSpacingMult, mSpacingAdd);
    159         assertNull(layout.getAlignment());
    160     }
    161 
    162     @Test
    163     public void testGetSpacingMultiplier() {
    164         Layout layout = new MockLayout(LAYOUT_TEXT, mTextPaint, mWidth, mAlign, -1, mSpacingAdd);
    165         assertEquals(-1.0f, layout.getSpacingMultiplier(), 0.0f);
    166 
    167         layout = new MockLayout(LAYOUT_TEXT, mTextPaint, mWidth, mAlign, 5, mSpacingAdd);
    168         assertEquals(5.0f, layout.getSpacingMultiplier(), 0.0f);
    169     }
    170 
    171     @Test
    172     public void testGetSpacingAdd() {
    173         Layout layout = new MockLayout(LAYOUT_TEXT, mTextPaint, mWidth, mAlign, mSpacingMult, -1);
    174         assertEquals(-1.0f, layout.getSpacingAdd(), 0.0f);
    175 
    176         layout = new MockLayout(LAYOUT_TEXT, mTextPaint, mWidth, mAlign, mSpacingMult, 20);
    177         assertEquals(20.0f, layout.getSpacingAdd(), 0.0f);
    178     }
    179 
    180     @Test
    181     public void testGetLineBounds() {
    182         Layout layout = new MockLayout(LAYOUT_TEXT, mTextPaint, mWidth,
    183                 mAlign, mSpacingMult, mSpacingAdd);
    184         Rect bounds = new Rect();
    185 
    186         assertEquals(32, layout.getLineBounds(2, bounds));
    187         assertEquals(0, bounds.left);
    188         assertEquals(mWidth, bounds.right);
    189         assertEquals(24, bounds.top);
    190         assertEquals(36, bounds.bottom);
    191     }
    192 
    193     @Test
    194     public void testGetLineForVertical() {
    195         Layout layout = new MockLayout(LAYOUT_TEXT, mTextPaint, mWidth,
    196                 mAlign, mSpacingMult, mSpacingAdd);
    197         assertEquals(0, layout.getLineForVertical(-1));
    198         assertEquals(0, layout.getLineForVertical(0));
    199         assertEquals(0, layout.getLineForVertical(LINE_COUNT));
    200         assertEquals(LINE_COUNT - 1, layout.getLineForVertical(1000));
    201     }
    202 
    203     @Test
    204     public void testGetLineForOffset() {
    205         Layout layout = new MockLayout(LAYOUT_TEXT, mTextPaint, mWidth,
    206                 mAlign, mSpacingMult, mSpacingAdd);
    207         assertEquals(0, layout.getLineForOffset(-1));
    208         assertEquals(1, layout.getLineForOffset(1));
    209         assertEquals(LINE_COUNT - 1, layout.getLineForOffset(LINE_COUNT - 1));
    210         assertEquals(LINE_COUNT - 1, layout.getLineForOffset(1000));
    211     }
    212 
    213     @Test
    214     public void testGetLineEnd() {
    215         Layout layout = new MockLayout(LAYOUT_TEXT, mTextPaint, mWidth,
    216                 mAlign, mSpacingMult, mSpacingAdd);
    217         assertEquals(2, layout.getLineEnd(1));
    218     }
    219 
    220     @Test
    221     public void testGetLineExtra_returnsZeroByDefault() {
    222         final String text = "a\nb\nc\n";
    223         final Layout layout = new MockLayout(LAYOUT_TEXT, mTextPaint, mWidth,
    224                 mAlign, 100 /* spacingMult*/, 100 /*spacingAdd*/);
    225         final int lineCount = text.split("\n").length;
    226         for (int i = 0; i < lineCount; i++) {
    227             assertEquals(0, layout.getLineExtra(i));
    228         }
    229     }
    230 
    231     @Test
    232     public void testGetLineVisibleEnd() {
    233         Layout layout = new MockLayout(LAYOUT_TEXT, mTextPaint, mWidth,
    234                 mAlign, mSpacingMult, mSpacingAdd);
    235 
    236         assertEquals(2, layout.getLineVisibleEnd(1));
    237         assertEquals(LINE_COUNT, layout.getLineVisibleEnd(LINE_COUNT - 1));
    238         assertEquals(LAYOUT_TEXT.length(), layout.getLineVisibleEnd(LAYOUT_TEXT.length() - 1));
    239         try {
    240             layout.getLineVisibleEnd(LAYOUT_TEXT.length());
    241             fail("should throw .StringIndexOutOfBoundsException here");
    242         } catch (StringIndexOutOfBoundsException e) {
    243         }
    244     }
    245 
    246     @Test
    247     public void testGetLineBottom() {
    248         Layout layout = new MockLayout(LAYOUT_TEXT, mTextPaint, mWidth,
    249                 mAlign, mSpacingMult, mSpacingAdd);
    250         assertEquals(LINE_HEIGHT, layout.getLineBottom(0));
    251     }
    252 
    253     @Test
    254     public void testGetLineBaseline() {
    255         Layout layout = new MockLayout(LAYOUT_TEXT, mTextPaint, mWidth,
    256                 mAlign, mSpacingMult, mSpacingAdd);
    257         assertEquals(8, layout.getLineBaseline(0));
    258     }
    259 
    260     @Test
    261     public void testGetLineAscent() {
    262         Layout layout = new MockLayout(LAYOUT_TEXT, mTextPaint, mWidth,
    263                 mAlign, mSpacingMult, mSpacingAdd);
    264         assertEquals(-8, layout.getLineAscent(0));
    265     }
    266 
    267     @Test
    268     public void testGetParagraphAlignment() {
    269         Layout layout = new MockLayout(LAYOUT_TEXT, mTextPaint, mWidth,
    270                 mAlign, mSpacingMult, mSpacingAdd);
    271         assertSame(mAlign, layout.getParagraphAlignment(0));
    272 
    273         layout = new MockLayout(mSpannedText, mTextPaint, mWidth,
    274                 mAlign, mSpacingMult, mSpacingAdd);
    275         assertSame(mAlign, layout.getParagraphAlignment(0));
    276         assertSame(mAlign, layout.getParagraphAlignment(1));
    277     }
    278 
    279     @Test
    280     public void testGetParagraphLeft() {
    281         Layout layout = new MockLayout(LAYOUT_TEXT, mTextPaint, mWidth,
    282                 mAlign, mSpacingMult, mSpacingAdd);
    283         assertEquals(0, layout.getParagraphLeft(0));
    284     }
    285 
    286     @Test
    287     public void testGetParagraphRight() {
    288         Layout layout = new MockLayout(LAYOUT_TEXT, mTextPaint, mWidth,
    289                 mAlign, mSpacingMult, mSpacingAdd);
    290         assertEquals(mWidth, layout.getParagraphRight(0));
    291     }
    292 
    293     @Test
    294     public void testGetSelectionWithEmptySelection() {
    295         final Layout layout = new MockLayout(LAYOUT_TEXT, mTextPaint, mWidth,
    296                 mAlign, mSpacingMult, mSpacingAdd);
    297 
    298         /*
    299          * When the selection is empty (i.e. the start and the end index are the same), we do not
    300          * expect any rectangles to be generated.
    301          */
    302 
    303         layout.getSelection(5 /* startIndex */, 5 /* endIndex */,
    304                 (left, top, right, bottom, textSelectionLayout) -> fail(
    305                         String.format(Locale.getDefault(),
    306                         "Did not expect any rectangles, got a rectangle with (left: %f,"
    307                                 + " top: %f), (right: %f, bottom: %f)",
    308                         left, top, right, bottom)));
    309     }
    310 
    311     @Test
    312     public void testGetSelectionWithASingleLineSelection() {
    313         final Layout layout = new StaticLayout("abc", mTextPaint, Integer.MAX_VALUE,
    314                 Alignment.ALIGN_LEFT, mSpacingMult, mSpacingAdd, false);
    315 
    316         final List<RectF> rectangles = new ArrayList<>();
    317 
    318         layout.getSelection(0 /* startIndex */, 1 /* endIndex */,
    319                 (left, top, right, bottom, textSelectionLayout) -> rectangles.add(
    320                         new RectF(left, top, right, bottom)));
    321 
    322         /*
    323          * The selection we expect will only cover the letter "a". Hence, we expect one rectangle
    324          * to be generated and this rectangle should start at the top left of the canvas and should
    325          * end somewhere to the right and down.
    326          *
    327          * | a | b c
    328          *
    329          */
    330 
    331         assertEquals(1, rectangles.size());
    332 
    333         final RectF rectangle = rectangles.get(0);
    334 
    335         assertEquals(0, rectangle.left, 0.0f);
    336         assertEquals(0, rectangle.top, 0.0f);
    337         assertTrue(rectangle.right > 0);
    338         assertTrue(rectangle.bottom > 0);
    339     }
    340 
    341     @Test
    342     public void
    343             testGetSelectionWithMultilineSelection_secondLineSelectionEndsBeforeFirstCharacter() {
    344         final Layout layout = new StaticLayout("a\nb\nc", mTextPaint, Integer.MAX_VALUE,
    345                 Alignment.ALIGN_LEFT, mSpacingMult, mSpacingAdd, false);
    346 
    347         final List<RectF> rectangles = new ArrayList<>();
    348 
    349         layout.getSelection(0 /* startIndex */, 2 /* endIndex */,
    350                 (left, top, right, bottom, textSelectionLayout) -> rectangles.add(
    351                         new RectF(left, top, right, bottom)));
    352 
    353         /*
    354          * The selection that will be selected is "a\n" - the selection starts at the beginning
    355          * of the first line and ends at the start of the second line. This means the selection
    356          * highlight will span from the beginning of the first line to the end of the first line
    357          * and will appear as a zero width line at the beginning of the second line.
    358          *
    359          * Hence, we expect three rectangles - one that will select the "a" on the first line,
    360          * one that will extend the selection from the "a" to the end of the first line and one
    361          * that will prepare the selection for the second line.
    362          *
    363          * | a | *topToEndOfLineRectangle* |
    364          * | b
    365          *   c
    366          */
    367 
    368         assertEquals(3, rectangles.size());
    369 
    370         final RectF topRectangle = rectangles.get(0);
    371         final RectF topToEndOfLineRectangle = rectangles.get(1);
    372         final RectF bottomLineStartRectangle = rectangles.get(2);
    373 
    374         assertFalse(topRectangle.intersect(bottomLineStartRectangle));
    375         assertTrue(topRectangle.top < bottomLineStartRectangle.top);
    376         assertTrue(topRectangle.left == bottomLineStartRectangle.left);
    377 
    378         assertFalse(topRectangle.intersect(topToEndOfLineRectangle));
    379         assertEquals(Integer.MAX_VALUE, topToEndOfLineRectangle.right, 1);
    380         assertTrue(topRectangle.top == topToEndOfLineRectangle.top);
    381         assertTrue(topRectangle.right == topToEndOfLineRectangle.left);
    382         assertTrue(topRectangle.bottom == topToEndOfLineRectangle.bottom);
    383 
    384         assertEquals(0, bottomLineStartRectangle.left, 0.0f);
    385         assertEquals(0, bottomLineStartRectangle.right, 0.0f);
    386     }
    387 
    388     @Test
    389     public void testGetSelectionWithMultilineSelection_secondLineSelectionEndsAfterACharacter() {
    390         final Layout layout = new StaticLayout("a\nb\nc", mTextPaint, Integer.MAX_VALUE,
    391                 Alignment.ALIGN_LEFT, mSpacingMult, mSpacingAdd, false);
    392 
    393         final List<RectF> rectangles = new ArrayList<>();
    394 
    395         layout.getSelection(0 /* startIndex */, 3 /* endIndex */,
    396                 (left, top, right, bottom, textSelectionLayout) -> rectangles.add(
    397                         new RectF(left, top, right, bottom)));
    398 
    399         /*
    400          * The selection that will be selected is "a\nb" - the selection starts at the beginning
    401          * of the first line and ends at the end of the letter "b". This means the selection
    402          * highlight will span from the beginning of the first line to the end of the first line
    403          * and will also cover the letter "b" on the second line.
    404          *
    405          * We expect four rectangles - one that will select the "a" on the first line,
    406          * one that will extend the selection from the "a" to the end of the first line the one
    407          * from the previous case that will prepare the selection for the second line and finally
    408          * one that will select the letter b.
    409          *
    410          *  | a | *topToEndOfLineRectangle* |
    411          * || b |
    412          *    c
    413          */
    414 
    415         assertEquals(4, rectangles.size());
    416 
    417         final RectF topRectangle = rectangles.get(0);
    418         final RectF topToEndOfLineRectangle = rectangles.get(1);
    419         final RectF bottomRectangle = rectangles.get(2);
    420         final RectF bottomLineStartRectangle = rectangles.get(3);
    421 
    422         assertTrue(topRectangle.top == topToEndOfLineRectangle.top);
    423         assertTrue(bottomLineStartRectangle.top == bottomRectangle.top);
    424         assertTrue(bottomLineStartRectangle.bottom == bottomRectangle.bottom);
    425         assertEquals(0, bottomLineStartRectangle.left, 0.0f);
    426         assertEquals(0, bottomLineStartRectangle.right, 0.0f);
    427         assertEquals(0, bottomRectangle.left, 0.0f);
    428         assertTrue(bottomRectangle.right > 0);
    429     }
    430 
    431     @Test
    432     public void testGetSelectionPathWithASingleLineSelection() {
    433         final Layout layout = new StaticLayout("abc", mTextPaint, Integer.MAX_VALUE,
    434                 Alignment.ALIGN_LEFT, mSpacingMult, mSpacingAdd, false);
    435 
    436         final List<RectF> rectangles = new ArrayList<>();
    437 
    438         layout.getSelection(0 /* startIndex */, 1 /* endIndex */,
    439                 (left, top, right, bottom, textSelectionLayout) -> rectangles.add(
    440                         new RectF(left, top, right, bottom)));
    441 
    442         /*
    443          * In the single line selection case, we expect that only one rectangle covering the letter
    444          * "a" will be generated. Hence, we expect that the generated path will only consist of
    445          * that rectangle as well.
    446          *
    447          * | a | b c
    448          *
    449          */
    450 
    451         assertEquals(1, rectangles.size());
    452 
    453         final RectF rectangle = rectangles.get(0);
    454 
    455         final Path generatedPath = new Path();
    456         layout.getSelectionPath(0 /* startIndex */, 1 /* endIndex */, generatedPath);
    457 
    458         final RectF pathRectangle = new RectF();
    459 
    460         assertTrue(generatedPath.isRect(pathRectangle));
    461         assertEquals(rectangle, pathRectangle);
    462     }
    463 
    464     @Test
    465     public void testGetSelection_latinTextDirection() {
    466         final Layout layout = new StaticLayout("abc", mTextPaint, Integer.MAX_VALUE,
    467                 Alignment.ALIGN_LEFT, mSpacingMult, mSpacingAdd, false);
    468 
    469         layout.getSelection(0 /* startIndex */, 2 /* endIndex */,
    470                 (left, top, right, bottom, textSelectionLayout) ->
    471                         assertEquals(Layout.TEXT_SELECTION_LAYOUT_LEFT_TO_RIGHT,
    472                                 textSelectionLayout));
    473     }
    474 
    475     @Test
    476     public void testGetSelection_arabicTextDirection() {
    477         final Layout layout = new StaticLayout("", mTextPaint, Integer.MAX_VALUE,
    478                 Alignment.ALIGN_LEFT, mSpacingMult, mSpacingAdd, false);
    479 
    480         layout.getSelection(0 /* startIndex */, 2 /* endIndex */,
    481                 (left, top, right, bottom, textSelectionLayout) ->
    482                         assertEquals(Layout.TEXT_SELECTION_LAYOUT_RIGHT_TO_LEFT,
    483                                 textSelectionLayout));
    484     }
    485 
    486     @Test
    487     public void testGetSelection_mixedLatinAndArabicTextDirection() {
    488         final Layout layout = new StaticLayout("abc", mTextPaint, Integer.MAX_VALUE,
    489                 Alignment.ALIGN_LEFT, mSpacingMult, mSpacingAdd, false);
    490 
    491         final List<Integer> layouts = new ArrayList<>(2);
    492 
    493         layout.getSelection(0 /* startIndex */, 6 /* endIndex */,
    494                 (left, top, right, bottom, textSelectionLayout) -> layouts.add(
    495                         textSelectionLayout));
    496 
    497         assertEquals(2, layouts.size());
    498         assertEquals(Layout.TEXT_SELECTION_LAYOUT_LEFT_TO_RIGHT, (long) layouts.get(0));
    499         assertEquals(Layout.TEXT_SELECTION_LAYOUT_RIGHT_TO_LEFT, (long) layouts.get(1));
    500     }
    501 
    502     @Test
    503     public void testIsSpanned() {
    504         MockLayout layout = new MockLayout(LAYOUT_TEXT, mTextPaint, mWidth,
    505                 mAlign, mSpacingMult, mSpacingAdd);
    506         // default is not spanned text
    507         assertFalse(layout.mockIsSpanned());
    508 
    509         // try to create a spanned text
    510         layout = new MockLayout(mSpannedText, mTextPaint, mWidth,
    511                 mAlign, mSpacingMult, mSpacingAdd);
    512         assertTrue(layout.mockIsSpanned());
    513     }
    514 
    515     private static final class MockLayout extends Layout {
    516         MockLayout(CharSequence text, TextPaint paint, int width,
    517                 Alignment align, float spacingmult, float spacingadd) {
    518             super(text, paint, width, align, spacingmult, spacingadd);
    519         }
    520 
    521         protected boolean mockIsSpanned() {
    522             return super.isSpanned();
    523         }
    524 
    525         @Override
    526         public int getBottomPadding() {
    527             return 0;
    528         }
    529 
    530         @Override
    531         public int getEllipsisCount(int line) {
    532             return 0;
    533         }
    534 
    535         @Override
    536         public int getEllipsisStart(int line) {
    537             return 0;
    538         }
    539 
    540         @Override
    541         public boolean getLineContainsTab(int line) {
    542             return false;
    543         }
    544 
    545         @Override
    546         public int getLineCount() {
    547             return LINE_COUNT;
    548         }
    549 
    550         @Override
    551         public int getLineDescent(int line) {
    552             return LINE_DESCENT;
    553         }
    554 
    555         @Override
    556         public Directions getLineDirections(int line) {
    557             return Layout.DIRS_ALL_LEFT_TO_RIGHT;
    558         }
    559 
    560         @Override
    561         public int getLineStart(int line) {
    562             if (line < 0) {
    563                 return 0;
    564             }
    565             return line;
    566         }
    567 
    568         @Override
    569         public int getLineTop(int line) {
    570             if (line < 0) {
    571                 return 0;
    572             }
    573             return LINE_HEIGHT * (line);
    574         }
    575 
    576         @Override
    577         public int getParagraphDirection(int line) {
    578             return 0;
    579         }
    580 
    581         @Override
    582         public int getTopPadding() {
    583             return 0;
    584         }
    585     }
    586 
    587     @Test
    588     public void testGetLineWidth() {
    589         Layout layout = new MockLayout(LAYOUT_TEXT, mTextPaint, mWidth,
    590                 mAlign, mSpacingMult, mSpacingAdd);
    591         for (int i = 0; i < LINE_COUNT; i++) {
    592             int start = layout.getLineStart(i);
    593             int end = layout.getLineEnd(i);
    594             String text = LAYOUT_TEXT.toString().substring(start, end);
    595             assertEquals(mTextPaint.measureText(text), layout.getLineWidth(i), 1.0f);
    596         }
    597     }
    598 
    599     @Test
    600     public void testGetCursorPath() {
    601         Layout layout = new MockLayout(LAYOUT_TEXT, mTextPaint, mWidth,
    602                 mAlign, mSpacingMult, mSpacingAdd);
    603         Path path = new Path();
    604         final float epsilon = 1.0f;
    605         for (int i = 0; i < LINE_COUNT; i++) {
    606             layout.getCursorPath(i, path, LAYOUT_TEXT);
    607             RectF bounds = new RectF();
    608             path.computeBounds(bounds, false);
    609             assertTrue(bounds.top >= layout.getLineTop(i) - epsilon);
    610             assertTrue(bounds.bottom <= layout.getLineBottom(i) + epsilon);
    611         }
    612     }
    613 
    614     @Test
    615     public void testDraw() {
    616         Layout layout = new MockLayout(LAYOUT_TEXT, mTextPaint, mWidth,
    617                 mAlign, mSpacingMult, mSpacingAdd);
    618         final int width = 256;
    619         final int height = 256;
    620         MockCanvas c = new MockCanvas(width, height);
    621         layout.draw(c);
    622         List<MockCanvas.DrawCommand> drawCommands = c.getDrawCommands();
    623         assertEquals(LINE_COUNT, drawCommands.size());
    624         for (int i = 0; i < LINE_COUNT; i++) {
    625             MockCanvas.DrawCommand drawCommand = drawCommands.get(i);
    626             int start = layout.getLineStart(i);
    627             int end = layout.getLineEnd(i);
    628             assertEquals(LAYOUT_TEXT.toString().substring(start, end), drawCommand.text);
    629             float expected_y = (i + 1) * LINE_HEIGHT - LINE_DESCENT;
    630             assertEquals(expected_y, drawCommand.y, 0.0f);
    631         }
    632     }
    633 
    634     private final class MockCanvas extends Canvas {
    635 
    636         class DrawCommand {
    637             public final String text;
    638             public final float x;
    639             public final float y;
    640 
    641             DrawCommand(String text, float x, float y) {
    642                 this.text = text;
    643                 this.x = x;
    644                 this.y = y;
    645             }
    646         }
    647 
    648         List<DrawCommand> mDrawCommands;
    649 
    650         MockCanvas(int width, int height) {
    651             super();
    652             mDrawCommands = new ArrayList<>();
    653             Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
    654             setBitmap(bitmap);
    655         }
    656 
    657         // Drawing text with either drawText or drawTextRun is valid; we don't care which.
    658         // We also don't care which of the string representations is used.
    659 
    660         @Override
    661         public void drawText(String text, int start, int end, float x, float y, Paint p) {
    662             mDrawCommands.add(new DrawCommand(text.substring(start, end), x, y));
    663         }
    664 
    665         @Override
    666         public void drawText(CharSequence text, int start, int end, float x, float y, Paint p) {
    667             drawText(text.toString(), start, end, x, y, p);
    668         }
    669 
    670         @Override
    671         public void drawText(char[] text, int index, int count, float x, float y, Paint p) {
    672             mDrawCommands.add(new DrawCommand(new String(text, index, count), x, y));
    673         }
    674 
    675         @Override
    676         public void drawTextRun(CharSequence text, int start, int end, int contextStart,
    677                 int contextEnd, float x, float y, boolean isRtl, Paint paint) {
    678             drawText(text, start, end, x, y, paint);
    679         }
    680 
    681         @Override
    682         public void drawTextRun(char[] text, int index, int count, int contextIndex,
    683                 int contextCount, float x, float y, boolean isRtl, Paint paint) {
    684             drawText(text, index, count, x, y, paint);
    685         }
    686 
    687         List<DrawCommand> getDrawCommands() {
    688             return mDrawCommands;
    689         }
    690     }
    691 }
    692 
    693