Home | History | Annotate | Download | only in cts
      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 android.text.Layout.Alignment.ALIGN_NORMAL;
     20 import static android.text.Spanned.SPAN_EXCLUSIVE_EXCLUSIVE;
     21 
     22 import static org.junit.Assert.assertEquals;
     23 import static org.junit.Assert.assertFalse;
     24 import static org.junit.Assert.assertNotNull;
     25 import static org.junit.Assert.assertTrue;
     26 import static org.junit.Assert.fail;
     27 import static org.mockito.Mockito.mock;
     28 
     29 import android.graphics.Paint.FontMetricsInt;
     30 import android.support.test.filters.SmallTest;
     31 import android.support.test.runner.AndroidJUnit4;
     32 import android.text.DynamicLayout;
     33 import android.text.Layout;
     34 import android.text.SpannableStringBuilder;
     35 import android.text.StaticLayout;
     36 import android.text.TextPaint;
     37 import android.text.TextUtils;
     38 import android.text.style.TypefaceSpan;
     39 
     40 import org.junit.Before;
     41 import org.junit.Test;
     42 import org.junit.runner.RunWith;
     43 
     44 @SmallTest
     45 @RunWith(AndroidJUnit4.class)
     46 public class DynamicLayoutTest {
     47 
     48     private static final float SPACING_MULT_NO_SCALE = 1.0f;
     49     private static final float SPACING_ADD_NO_SCALE = 0.0f;
     50     private static final int DEFAULT_OUTER_WIDTH = 150;
     51     private static final int ELLIPSIZE_WIDTH = 8;
     52     private static final CharSequence SINGLELINE_CHAR_SEQUENCE = "......";
     53     private static final String[] TEXT = {"CharSequence\n", "Char\tSequence\n", "CharSequence"};
     54     private static final CharSequence MULTLINE_CHAR_SEQUENCE = TEXT[0] + TEXT[1] + TEXT[2];
     55     private static final Layout.Alignment DEFAULT_ALIGN = Layout.Alignment.ALIGN_CENTER;
     56     private TextPaint mDefaultPaint;
     57 
     58     private static final int LINE0 = 0;
     59     private static final int LINE0_TOP = 0;
     60     private static final int LINE1 = 1;
     61     private static final int LINE2 = 2;
     62     private static final int LINE3 = 3;
     63 
     64     private static final int ELLIPSIS_UNDEFINED = 0x80000000;
     65 
     66     private DynamicLayout mDynamicLayout;
     67 
     68     @Before
     69     public void setup() {
     70         mDefaultPaint = new TextPaint();
     71         mDynamicLayout = new DynamicLayout(MULTLINE_CHAR_SEQUENCE,
     72                 mDefaultPaint,
     73                 DEFAULT_OUTER_WIDTH,
     74                 DEFAULT_ALIGN,
     75                 SPACING_MULT_NO_SCALE,
     76                 SPACING_ADD_NO_SCALE,
     77                 true);
     78     }
     79 
     80     @Test
     81     public void testConstructors() {
     82         new DynamicLayout(SINGLELINE_CHAR_SEQUENCE,
     83                 MULTLINE_CHAR_SEQUENCE,
     84                 mDefaultPaint,
     85                 DEFAULT_OUTER_WIDTH,
     86                 DEFAULT_ALIGN,
     87                 SPACING_MULT_NO_SCALE,
     88                 SPACING_ADD_NO_SCALE,
     89                 true);
     90         new DynamicLayout(SINGLELINE_CHAR_SEQUENCE,
     91                 MULTLINE_CHAR_SEQUENCE,
     92                 mDefaultPaint,
     93                 DEFAULT_OUTER_WIDTH,
     94                 DEFAULT_ALIGN,
     95                 SPACING_MULT_NO_SCALE,
     96                 SPACING_ADD_NO_SCALE,
     97                 true,
     98                 TextUtils.TruncateAt.START,
     99                 DEFAULT_OUTER_WIDTH);
    100         new DynamicLayout(MULTLINE_CHAR_SEQUENCE,
    101                 mDefaultPaint,
    102                 DEFAULT_OUTER_WIDTH,
    103                 DEFAULT_ALIGN,
    104                 SPACING_MULT_NO_SCALE,
    105                 SPACING_ADD_NO_SCALE,
    106                 true);
    107     }
    108 
    109     @Test
    110     public void testEllipsis() {
    111         final DynamicLayout dynamicLayout = new DynamicLayout(SINGLELINE_CHAR_SEQUENCE,
    112                 MULTLINE_CHAR_SEQUENCE,
    113                 mDefaultPaint,
    114                 DEFAULT_OUTER_WIDTH,
    115                 DEFAULT_ALIGN,
    116                 SPACING_MULT_NO_SCALE,
    117                 SPACING_ADD_NO_SCALE,
    118                 true,
    119                 TextUtils.TruncateAt.START,
    120                 DEFAULT_OUTER_WIDTH);
    121         assertEquals(0, dynamicLayout.getEllipsisCount(LINE1));
    122         assertEquals(ELLIPSIS_UNDEFINED, dynamicLayout.getEllipsisStart(LINE1));
    123         assertEquals(DEFAULT_OUTER_WIDTH, dynamicLayout.getEllipsizedWidth());
    124     }
    125 
    126     /*
    127      * Test whether include the padding to calculate the layout.
    128      * 1. Include padding while calculate the layout.
    129      * 2. Don't include padding while calculate the layout.
    130      */
    131     @Test
    132     public void testIncludePadding() {
    133         final FontMetricsInt fontMetricsInt = mDefaultPaint.getFontMetricsInt();
    134 
    135         DynamicLayout dynamicLayout = new DynamicLayout(SINGLELINE_CHAR_SEQUENCE,
    136                 mDefaultPaint,
    137                 DEFAULT_OUTER_WIDTH,
    138                 DEFAULT_ALIGN,
    139                 SPACING_MULT_NO_SCALE,
    140                 SPACING_ADD_NO_SCALE,
    141                 true);
    142         assertEquals(fontMetricsInt.top - fontMetricsInt.ascent, dynamicLayout.getTopPadding());
    143         assertEquals(fontMetricsInt.bottom - fontMetricsInt.descent,
    144                 dynamicLayout.getBottomPadding());
    145 
    146         dynamicLayout = new DynamicLayout(SINGLELINE_CHAR_SEQUENCE,
    147                 mDefaultPaint,
    148                 DEFAULT_OUTER_WIDTH,
    149                 DEFAULT_ALIGN,
    150                 SPACING_MULT_NO_SCALE,
    151                 SPACING_ADD_NO_SCALE,
    152                 false);
    153         assertEquals(0, dynamicLayout.getTopPadding());
    154         assertEquals(0, dynamicLayout.getBottomPadding());
    155     }
    156 
    157     /*
    158      * Test the line count and whether include the Tab the layout.
    159      * 1. Include Tab. 2. Don't include Tab Use the Y-coordinate to calculate the line number
    160      * Test the line top
    161      * 1. the Y-coordinate of line top.2. the Y-coordinate of baseline.
    162      */
    163     @Test
    164     public void testLineLayout() {
    165         assertEquals(TEXT.length, mDynamicLayout.getLineCount());
    166         assertFalse(mDynamicLayout.getLineContainsTab(LINE0));
    167         assertTrue(mDynamicLayout.getLineContainsTab(LINE1));
    168 
    169         assertEquals(LINE0_TOP, mDynamicLayout.getLineTop(LINE0));
    170 
    171         assertEquals(mDynamicLayout.getLineBottom(LINE0), mDynamicLayout.getLineTop(LINE1));
    172         assertEquals(mDynamicLayout.getLineBottom(LINE1), mDynamicLayout.getLineTop(LINE2));
    173         assertEquals(mDynamicLayout.getLineBottom(LINE2), mDynamicLayout.getLineTop(LINE3));
    174 
    175         try {
    176             assertEquals(mDynamicLayout.getLineBottom(mDynamicLayout.getLineCount()),
    177                     mDynamicLayout.getLineTop(mDynamicLayout.getLineCount() + 1));
    178             fail("Test DynamicLayout fail, should throw IndexOutOfBoundsException.");
    179         } catch (IndexOutOfBoundsException e) {
    180             // expected
    181         }
    182 
    183         assertEquals(mDynamicLayout.getLineDescent(LINE0) - mDynamicLayout.getLineAscent(LINE0),
    184                 mDynamicLayout.getLineBottom(LINE0));
    185 
    186         assertEquals(mDynamicLayout.getLineDescent(LINE1) - mDynamicLayout.getLineAscent(LINE1),
    187                 mDynamicLayout.getLineBottom(LINE1) - mDynamicLayout.getLineBottom(LINE0));
    188 
    189         assertEquals(mDynamicLayout.getLineDescent(LINE2) - mDynamicLayout.getLineAscent(LINE2),
    190                 mDynamicLayout.getLineBottom(LINE2) - mDynamicLayout.getLineBottom(LINE1));
    191 
    192         assertEquals(LINE0, mDynamicLayout.getLineForVertical(mDynamicLayout.getLineTop(LINE0)));
    193 
    194         assertNotNull(mDynamicLayout.getLineDirections(LINE0));
    195         assertEquals(Layout.DIR_LEFT_TO_RIGHT, mDynamicLayout.getParagraphDirection(LINE0));
    196 
    197         assertEquals(0, mDynamicLayout.getLineStart(LINE0));
    198         assertEquals(TEXT[0].length(), mDynamicLayout.getLineStart(LINE1));
    199         assertEquals(TEXT[0].length() + TEXT[1].length(), mDynamicLayout.getLineStart(LINE2));
    200     }
    201 
    202     @Test
    203     public void testLineSpacing() {
    204         SpannableStringBuilder text = new SpannableStringBuilder("a\nb\nc");
    205         final float spacingMultiplier = 2f;
    206         final float spacingAdd = 4;
    207         final int width = 1000;
    208         final TextPaint textPaint = new TextPaint();
    209         // create the DynamicLayout
    210         final DynamicLayout dynamicLayout = new DynamicLayout(text,
    211                 textPaint,
    212                 width,
    213                 ALIGN_NORMAL,
    214                 spacingMultiplier,
    215                 spacingAdd,
    216                 false /*includepad*/);
    217 
    218         // create a StaticLayout with same text, this will define the expectations
    219         Layout expected = createStaticLayout(text.toString(), textPaint, width, spacingAdd,
    220                 spacingMultiplier);
    221         assertLineSpecs(expected, dynamicLayout);
    222 
    223         // add a new line to the end, DynamicLayout will re-calculate
    224         text = text.append("\nd");
    225         expected = createStaticLayout(text.toString(), textPaint, width, spacingAdd,
    226                 spacingMultiplier);
    227         assertLineSpecs(expected, dynamicLayout);
    228 
    229         // insert a next line and a char as the new second line
    230         text = text.insert(TextUtils.indexOf(text, '\n') + 1, "a1\n");
    231         expected = createStaticLayout(text.toString(), textPaint, width, spacingAdd,
    232                 spacingMultiplier);
    233         assertLineSpecs(expected, dynamicLayout);
    234     }
    235 
    236     @Test
    237     public void testLineSpacing_textEndingWithNextLine() {
    238         final SpannableStringBuilder text = new SpannableStringBuilder("a\n");
    239         final float spacingMultiplier = 2f;
    240         final float spacingAdd = 4f;
    241         final int width = 1000;
    242         final TextPaint textPaint = new TextPaint();
    243         // create the DynamicLayout
    244         final DynamicLayout dynamicLayout = new DynamicLayout(text,
    245                 textPaint,
    246                 width,
    247                 ALIGN_NORMAL,
    248                 spacingMultiplier,
    249                 spacingAdd,
    250                 false /*includepad*/);
    251 
    252         // create a StaticLayout with same text, this will define the expectations
    253         final Layout expected = createStaticLayout(text.toString(), textPaint, width, spacingAdd,
    254                 spacingMultiplier);
    255         assertLineSpecs(expected, dynamicLayout);
    256     }
    257 
    258     private Layout createStaticLayout(CharSequence text, TextPaint textPaint, int width,
    259             float spacingAdd, float spacingMultiplier) {
    260         return StaticLayout.Builder.obtain(text, 0,
    261                 text.length(), textPaint, width)
    262                 .setAlignment(ALIGN_NORMAL)
    263                 .setIncludePad(false)
    264                 .setLineSpacing(spacingAdd, spacingMultiplier)
    265                 .build();
    266     }
    267 
    268     private void assertLineSpecs(Layout expected, DynamicLayout actual) {
    269         final int lineCount = expected.getLineCount();
    270         assertTrue(lineCount > 1);
    271         assertEquals(lineCount, actual.getLineCount());
    272 
    273         for (int i = 0; i < lineCount; i++) {
    274             assertEquals(expected.getLineTop(i), actual.getLineTop(i));
    275             assertEquals(expected.getLineDescent(i), actual.getLineDescent(i));
    276             assertEquals(expected.getLineBaseline(i), actual.getLineBaseline(i));
    277             assertEquals(expected.getLineBottom(i), actual.getLineBottom(i));
    278         }
    279     }
    280 
    281     @Test
    282     public void testLineSpacing_notAffectedByPreviousEllipsization() {
    283         // Create an ellipsized DynamicLayout, but throw it away.
    284         final String ellipsizedText = "Some arbitrary relatively long text";
    285         final DynamicLayout ellipsizedLayout = new DynamicLayout(
    286                 ellipsizedText,
    287                 ellipsizedText,
    288                 mDefaultPaint,
    289                 1 << 20 /* width */,
    290                 DEFAULT_ALIGN,
    291                 SPACING_MULT_NO_SCALE,
    292                 SPACING_ADD_NO_SCALE,
    293                 true /* include pad */,
    294                 TextUtils.TruncateAt.END,
    295                 2 * (int) mDefaultPaint.getTextSize() /* ellipsizedWidth */);
    296 
    297         // Now try to measure linespacing in a non-ellipsized DynamicLayout.
    298         final String text = "a\nb\nc";
    299         final float spacingMultiplier = 2f;
    300         final float spacingAdd = 4f;
    301         final int width = 1000;
    302         final TextPaint textPaint = new TextPaint();
    303         // create the DynamicLayout
    304         final DynamicLayout dynamicLayout = new DynamicLayout(text,
    305                 textPaint,
    306                 width,
    307                 ALIGN_NORMAL,
    308                 spacingMultiplier,
    309                 spacingAdd,
    310                 false /*includepad*/);
    311 
    312         // create a StaticLayout with same text, this will define the expectations
    313         Layout expected = createStaticLayout(text.toString(), textPaint, width, spacingAdd,
    314                 spacingMultiplier);
    315         assertLineSpecs(expected, dynamicLayout);
    316     }
    317 
    318     @Test
    319     public void testBuilder_obtain() {
    320         final DynamicLayout.Builder builder = DynamicLayout.Builder.obtain(MULTLINE_CHAR_SEQUENCE,
    321                 mDefaultPaint, DEFAULT_OUTER_WIDTH);
    322         final DynamicLayout layout = builder.build();
    323         // Check values passed to obtain().
    324         assertEquals(MULTLINE_CHAR_SEQUENCE, layout.getText());
    325         assertEquals(mDefaultPaint, layout.getPaint());
    326         assertEquals(DEFAULT_OUTER_WIDTH, layout.getWidth());
    327         // Check default values.
    328         assertEquals(Layout.Alignment.ALIGN_NORMAL, layout.getAlignment());
    329         assertEquals(0.0f, layout.getSpacingAdd(), 0.0f);
    330         assertEquals(1.0f, layout.getSpacingMultiplier(), 0.0f);
    331         assertEquals(DEFAULT_OUTER_WIDTH, layout.getEllipsizedWidth());
    332     }
    333 
    334     @Test(expected = NullPointerException.class)
    335     public void testBuilder_obtainWithNullText() {
    336         final DynamicLayout.Builder builder = DynamicLayout.Builder.obtain(null, mDefaultPaint, 0);
    337         final DynamicLayout layout = builder.build();
    338     }
    339 
    340     @Test(expected = NullPointerException.class)
    341     public void testBuilder_obtainWithNullPaint() {
    342         final DynamicLayout.Builder builder = DynamicLayout.Builder.obtain(MULTLINE_CHAR_SEQUENCE,
    343                 null, 0);
    344         final DynamicLayout layout = builder.build();
    345     }
    346 
    347     @Test
    348     public void testBuilder_setDisplayTest() {
    349         final DynamicLayout.Builder builder = DynamicLayout.Builder.obtain(MULTLINE_CHAR_SEQUENCE,
    350                 mDefaultPaint, DEFAULT_OUTER_WIDTH);
    351         builder.setDisplayText(SINGLELINE_CHAR_SEQUENCE);
    352         final DynamicLayout layout = builder.build();
    353         assertEquals(SINGLELINE_CHAR_SEQUENCE, layout.getText());
    354     }
    355 
    356     @Test
    357     public void testBuilder_setAlignment() {
    358         final DynamicLayout.Builder builder = DynamicLayout.Builder.obtain(MULTLINE_CHAR_SEQUENCE,
    359                 mDefaultPaint, DEFAULT_OUTER_WIDTH);
    360         builder.setAlignment(DEFAULT_ALIGN);
    361         final DynamicLayout layout = builder.build();
    362         assertEquals(DEFAULT_ALIGN, layout.getAlignment());
    363     }
    364 
    365     @Test
    366     public void testBuilder_setLineSpacing() {
    367         final DynamicLayout.Builder builder = DynamicLayout.Builder.obtain(MULTLINE_CHAR_SEQUENCE,
    368                 mDefaultPaint, DEFAULT_OUTER_WIDTH);
    369         builder.setLineSpacing(1.0f, 2.0f);
    370         final DynamicLayout layout = builder.build();
    371         assertEquals(1.0f, layout.getSpacingAdd(), 0.0f);
    372         assertEquals(2.0f, layout.getSpacingMultiplier(), 0.0f);
    373     }
    374 
    375     @Test
    376     public void testBuilder_ellipsization() {
    377         final DynamicLayout.Builder builder = DynamicLayout.Builder.obtain(MULTLINE_CHAR_SEQUENCE,
    378                 mDefaultPaint, DEFAULT_OUTER_WIDTH);
    379         builder.setEllipsize(TextUtils.TruncateAt.END)
    380                 .setEllipsizedWidth(ELLIPSIZE_WIDTH);
    381         final DynamicLayout layout = builder.build();
    382         assertEquals(ELLIPSIZE_WIDTH, layout.getEllipsizedWidth());
    383         assertEquals(DEFAULT_OUTER_WIDTH, layout.getWidth());
    384         for (int i = 0; i < TEXT.length; i++) {
    385             if (i == TEXT.length - 1) { // last line
    386                 assertTrue(layout.getEllipsisCount(i) > 0);
    387             } else {
    388                 assertEquals(0, layout.getEllipsisCount(i));
    389             }
    390         }
    391     }
    392 
    393     @Test
    394     public void testBuilder_otherSetters() {
    395         // Setter methods that cannot be directly tested.
    396         // setBreakStrategy, setHyphenationFrequency, setIncludePad, and setJustificationMode.
    397         final DynamicLayout.Builder builder = DynamicLayout.Builder.obtain(MULTLINE_CHAR_SEQUENCE,
    398                 mDefaultPaint, DEFAULT_OUTER_WIDTH);
    399         builder.setBreakStrategy(Layout.BREAK_STRATEGY_HIGH_QUALITY)
    400                 .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_FULL)
    401                 .setIncludePad(true)
    402                 .setJustificationMode(Layout.JUSTIFICATION_MODE_INTER_WORD);
    403         final DynamicLayout layout = builder.build();
    404         assertNotNull(layout);
    405     }
    406 
    407     @Test
    408     public void testReflow_afterSpanChangedShouldNotThrowException() {
    409         final SpannableStringBuilder builder = new SpannableStringBuilder("crash crash crash!!");
    410 
    411         final TypefaceSpan span = mock(TypefaceSpan.class);
    412         builder.setSpan(span, 1, 4, SPAN_EXCLUSIVE_EXCLUSIVE);
    413 
    414         final DynamicLayout layout = DynamicLayout.Builder.obtain(builder,
    415                 new TextPaint(), Integer.MAX_VALUE).build();
    416         try {
    417             builder.insert(1, "Hello there\n\n");
    418         } catch (Throwable e) {
    419             throw new RuntimeException("Inserting text into DynamicLayout should not crash", e);
    420         }
    421     }
    422 
    423 }
    424