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