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