StaticLayoutTest.java revision 6ad5a7a7c78799ecb306cb97d979bdb98cc52d15
1/*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5 * use this file except in compliance with the License. You may obtain a copy of
6 * 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, WITHOUT
12 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 * License for the specific language governing permissions and limitations under
14 * the License.
15 */
16
17package android.text;
18
19import android.graphics.Paint.FontMetricsInt;
20import android.test.suitebuilder.annotation.SmallTest;
21import android.text.Layout.Alignment;
22import static android.text.Layout.Alignment.*;
23import android.util.Log;
24
25import junit.framework.TestCase;
26
27/**
28 * Tests StaticLayout vertical metrics behavior.
29 */
30public class StaticLayoutTest extends TestCase {
31
32    /**
33     * Basic test showing expected behavior and relationship between font
34     * metrics and line metrics.
35     */
36    @SmallTest
37    public void testGetters1() {
38        LayoutBuilder b = builder();
39        FontMetricsInt fmi = b.paint.getFontMetricsInt();
40
41        // check default paint
42        Log.i("TG1:paint", fmi.toString());
43
44        Layout l = b.build();
45        assertVertMetrics(l, 0, 0,
46                fmi.ascent, fmi.descent);
47
48        // other quick metrics
49        assertEquals(0, l.getLineStart(0));
50        assertEquals(Layout.DIR_LEFT_TO_RIGHT, l.getParagraphDirection(0));
51        assertEquals(false, l.getLineContainsTab(0));
52        assertEquals(Layout.DIRS_ALL_LEFT_TO_RIGHT, l.getLineDirections(0));
53        assertEquals(0, l.getEllipsisCount(0));
54        assertEquals(0, l.getEllipsisStart(0));
55        assertEquals(b.width, l.getEllipsizedWidth());
56    }
57
58    /**
59     * Basic test showing effect of includePad = true with 1 line.
60     * Top and bottom padding are affected, as is the line descent and height.
61     */
62    @SmallTest
63    public void testGetters2() {
64        LayoutBuilder b = builder()
65            .setIncludePad(true);
66        FontMetricsInt fmi = b.paint.getFontMetricsInt();
67
68        Layout l = b.build();
69        assertVertMetrics(l, fmi.top - fmi.ascent, fmi.bottom - fmi.descent,
70                fmi.top, fmi.bottom);
71    }
72
73    /**
74     * Basic test showing effect of includePad = true wrapping to 2 lines.
75     * Ascent of top line and descent of bottom line are affected.
76     */
77    @SmallTest
78    public void testGetters3() {
79        LayoutBuilder b = builder()
80            .setIncludePad(true)
81            .setWidth(50);
82        FontMetricsInt fmi = b.paint.getFontMetricsInt();
83
84        Layout l =  b.build();
85        assertVertMetrics(l, fmi.top - fmi.ascent, fmi.bottom - fmi.descent,
86            fmi.top, fmi.descent,
87            fmi.ascent, fmi.bottom);
88    }
89
90    /**
91     * Basic test showing effect of includePad = true wrapping to 3 lines.
92     * First line ascent is top, bottom line descent is bottom.
93     */
94    @SmallTest
95    public void testGetters4() {
96        LayoutBuilder b = builder()
97            .setText("This is a longer test")
98            .setIncludePad(true)
99            .setWidth(50);
100        FontMetricsInt fmi = b.paint.getFontMetricsInt();
101
102        Layout l = b.build();
103        assertVertMetrics(l, fmi.top - fmi.ascent, fmi.bottom - fmi.descent,
104                fmi.top, fmi.descent,
105                fmi.ascent, fmi.descent,
106                fmi.ascent, fmi.bottom);
107    }
108
109    /**
110     * Basic test showing effect of includePad = true wrapping to 3 lines and
111     * large text. See effect of leading. Currently, we don't expect there to
112     * even be non-zero leading.
113     */
114    @SmallTest
115    public void testGetters5() {
116        LayoutBuilder b = builder()
117            .setText("This is a longer test")
118            .setIncludePad(true)
119            .setWidth(150);
120        b.paint.setTextSize(36);
121        FontMetricsInt fmi = b.paint.getFontMetricsInt();
122
123        if (fmi.leading == 0) { // nothing to test
124            Log.i("TG5", "leading is 0, skipping test");
125            return;
126        }
127
128        // So far, leading is not used, so this is the same as TG4.  If we start
129        // using leading, this will fail.
130        Layout l = b.build();
131        assertVertMetrics(l, fmi.top - fmi.ascent, fmi.bottom - fmi.descent,
132                fmi.top, fmi.descent,
133                fmi.ascent, fmi.descent,
134                fmi.ascent, fmi.bottom);
135    }
136
137    /**
138     * Basic test showing effect of includePad = true, spacingAdd = 2, wrapping
139     * to 3 lines.
140     */
141    @SmallTest
142    public void testGetters6() {
143        int spacingAdd = 2; // int so expressions return int
144        LayoutBuilder b = builder()
145            .setText("This is a longer test")
146            .setIncludePad(true)
147            .setWidth(50)
148            .setSpacingAdd(spacingAdd);
149        FontMetricsInt fmi = b.paint.getFontMetricsInt();
150
151        Layout l = b.build();
152        assertVertMetrics(l, fmi.top - fmi.ascent, fmi.bottom - fmi.descent,
153                fmi.top, fmi.descent + spacingAdd,
154                fmi.ascent, fmi.descent + spacingAdd,
155                fmi.ascent, fmi.bottom + spacingAdd);
156    }
157
158    /**
159     * Basic test showing effect of includePad = true, spacingAdd = 2,
160     * spacingMult = 1.5, wrapping to 3 lines.
161     */
162    @SmallTest
163    public void testGetters7() {
164        LayoutBuilder b = builder()
165            .setText("This is a longer test")
166            .setIncludePad(true)
167            .setWidth(50)
168            .setSpacingAdd(2)
169            .setSpacingMult(1.5f);
170        FontMetricsInt fmi = b.paint.getFontMetricsInt();
171        Scaler s = new Scaler(b.spacingMult, b.spacingAdd);
172
173        Layout l = b.build();
174        assertVertMetrics(l, fmi.top - fmi.ascent, fmi.bottom - fmi.descent,
175                fmi.top, fmi.descent + s.scale(fmi.descent - fmi.top),
176                fmi.ascent, fmi.descent + s.scale(fmi.descent - fmi.ascent),
177                fmi.ascent, fmi.bottom + s.scale(fmi.bottom - fmi.ascent));
178    }
179
180    /**
181     * Basic test showing effect of includePad = true, spacingAdd = 0,
182     * spacingMult = 0.8 when wrapping to 3 lines.
183     */
184    @SmallTest
185    public void testGetters8() {
186        LayoutBuilder b = builder()
187            .setText("This is a longer test")
188            .setIncludePad(true)
189            .setWidth(50)
190            .setSpacingAdd(2)
191            .setSpacingMult(.8f);
192        FontMetricsInt fmi = b.paint.getFontMetricsInt();
193        Scaler s = new Scaler(b.spacingMult, b.spacingAdd);
194
195        Layout l = b.build();
196        assertVertMetrics(l, fmi.top - fmi.ascent, fmi.bottom - fmi.descent,
197                fmi.top, fmi.descent + s.scale(fmi.descent - fmi.top),
198                fmi.ascent, fmi.descent + s.scale(fmi.descent - fmi.ascent),
199                fmi.ascent, fmi.bottom + s.scale(fmi.bottom - fmi.ascent));
200    }
201
202    // ----- test utility classes and methods -----
203
204    // Models the effect of the scale and add parameters.  I think the current
205    // implementation misbehaves.
206    private static class Scaler {
207        private final float sMult;
208        private final float sAdd;
209
210        Scaler(float sMult, float sAdd) {
211            this.sMult = sMult - 1;
212            this.sAdd = sAdd;
213        }
214
215        public int scale(float height) {
216            int altVal = (int)(height * sMult + sAdd + 0.5); // existing impl
217            int rndVal = Math.round(height * sMult + sAdd);
218            if (altVal != rndVal) {
219                Log.i("Scale", "expected scale: " + rndVal +
220                        " != returned scale: " + altVal);
221            }
222            return altVal; // existing implementation
223        }
224    }
225
226    private static LayoutBuilder builder() {
227        return new LayoutBuilder();
228    }
229
230    private static class LayoutBuilder {
231        String text = "This is a test";
232        TextPaint paint = new TextPaint(); // default
233        int width = 100;
234        Alignment align = ALIGN_NORMAL;
235        float spacingMult = 1;
236        float spacingAdd = 0;
237        boolean includePad = false;
238
239        LayoutBuilder setText(String text) {
240            this.text = text;
241            return this;
242        }
243
244        LayoutBuilder setPaint(TextPaint paint) {
245            this.paint = paint;
246            return this;
247        }
248
249        LayoutBuilder setWidth(int width) {
250            this.width = width;
251            return this;
252        }
253
254        LayoutBuilder setAlignment(Alignment align) {
255            this.align = align;
256            return this;
257        }
258
259        LayoutBuilder setSpacingMult(float spacingMult) {
260            this.spacingMult = spacingMult;
261            return this;
262        }
263
264        LayoutBuilder setSpacingAdd(float spacingAdd) {
265            this.spacingAdd = spacingAdd;
266            return this;
267        }
268
269        LayoutBuilder setIncludePad(boolean includePad) {
270            this.includePad = includePad;
271            return this;
272        }
273
274       Layout build() {
275            return  new StaticLayout(text, paint, width, align, spacingMult,
276                spacingAdd, includePad);
277        }
278    }
279
280    private void assertVertMetrics(Layout l, int topPad, int botPad, int... values) {
281        assertTopBotPadding(l, topPad, botPad);
282        assertLinesMetrics(l, values);
283    }
284
285    private void assertLinesMetrics(Layout l, int... values) {
286        // sanity check
287        if ((values.length & 0x1) != 0) {
288            throw new IllegalArgumentException(String.valueOf(values.length));
289        }
290
291        int lines = values.length >> 1;
292        assertEquals(lines, l.getLineCount());
293
294        int t = 0;
295        for (int i = 0, n = 0; i < lines; ++i, n += 2) {
296            int a = values[n];
297            int d = values[n+1];
298            int h = -a + d;
299            assertLineMetrics(l, i, t, a, d, h);
300            t += h;
301        }
302
303        assertEquals(t, l.getHeight());
304    }
305
306    private void assertLineMetrics(Layout l, int line,
307            int top, int ascent, int descent, int height) {
308        String info = "line " + line;
309        assertEquals(info, top, l.getLineTop(line));
310        assertEquals(info, ascent, l.getLineAscent(line));
311        assertEquals(info, descent, l.getLineDescent(line));
312        assertEquals(info, height, l.getLineBottom(line) - top);
313    }
314
315    private void assertTopBotPadding(Layout l, int topPad, int botPad) {
316        assertEquals(topPad, l.getTopPadding());
317        assertEquals(botPad, l.getBottomPadding());
318    }
319}
320