diff --git a/core/tests/coretests/src/android/text/StaticLayoutTest.java b/core/tests/coretests/src/android/text/StaticLayoutTest.java new file mode 100644 index 0000000000000..1e4db3dea1f72 --- /dev/null +++ b/core/tests/coretests/src/android/text/StaticLayoutTest.java @@ -0,0 +1,319 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package android.text; + +import android.graphics.Paint.FontMetricsInt; +import android.test.suitebuilder.annotation.SmallTest; +import android.text.Layout.Alignment; +import static android.text.Layout.Alignment.*; +import android.util.Log; + +import junit.framework.TestCase; + +/** + * Tests StaticLayout vertical metrics behavior. + */ +public class StaticLayoutTest extends TestCase { + + /** + * Basic test showing expected behavior and relationship between font + * metrics and line metrics. + */ + @SmallTest + public void testGetters1() { + LayoutBuilder b = builder(); + FontMetricsInt fmi = b.paint.getFontMetricsInt(); + + // check default paint + Log.i("TG1:paint", fmi.toString()); + + Layout l = b.build(); + assertVertMetrics(l, 0, 0, + fmi.ascent, fmi.descent); + + // other quick metrics + assertEquals(0, l.getLineStart(0)); + assertEquals(Layout.DIR_LEFT_TO_RIGHT, l.getParagraphDirection(0)); + assertEquals(false, l.getLineContainsTab(0)); + assertEquals(Layout.DIRS_ALL_LEFT_TO_RIGHT, l.getLineDirections(0)); + assertEquals(0, l.getEllipsisCount(0)); + assertEquals(0, l.getEllipsisStart(0)); + assertEquals(b.width, l.getEllipsizedWidth()); + } + + /** + * Basic test showing effect of includePad = true with 1 line. + * Top and bottom padding are affected, as is the line descent and height. + */ + @SmallTest + public void testGetters2() { + LayoutBuilder b = builder() + .setIncludePad(true); + FontMetricsInt fmi = b.paint.getFontMetricsInt(); + + Layout l = b.build(); + assertVertMetrics(l, fmi.top - fmi.ascent, fmi.bottom - fmi.descent, + fmi.top, fmi.bottom); + } + + /** + * Basic test showing effect of includePad = true wrapping to 2 lines. + * Ascent of top line and descent of bottom line are affected. + */ + @SmallTest + public void testGetters3() { + LayoutBuilder b = builder() + .setIncludePad(true) + .setWidth(50); + FontMetricsInt fmi = b.paint.getFontMetricsInt(); + + Layout l = b.build(); + assertVertMetrics(l, fmi.top - fmi.ascent, fmi.bottom - fmi.descent, + fmi.top, fmi.descent, + fmi.ascent, fmi.bottom); + } + + /** + * Basic test showing effect of includePad = true wrapping to 3 lines. + * First line ascent is top, bottom line descent is bottom. + */ + @SmallTest + public void testGetters4() { + LayoutBuilder b = builder() + .setText("This is a longer test") + .setIncludePad(true) + .setWidth(50); + FontMetricsInt fmi = b.paint.getFontMetricsInt(); + + Layout l = b.build(); + assertVertMetrics(l, fmi.top - fmi.ascent, fmi.bottom - fmi.descent, + fmi.top, fmi.descent, + fmi.ascent, fmi.descent, + fmi.ascent, fmi.bottom); + } + + /** + * Basic test showing effect of includePad = true wrapping to 3 lines and + * large text. See effect of leading. Currently, we don't expect there to + * even be non-zero leading. + */ + @SmallTest + public void testGetters5() { + LayoutBuilder b = builder() + .setText("This is a longer test") + .setIncludePad(true) + .setWidth(150); + b.paint.setTextSize(36); + FontMetricsInt fmi = b.paint.getFontMetricsInt(); + + if (fmi.leading == 0) { // nothing to test + Log.i("TG5", "leading is 0, skipping test"); + return; + } + + // So far, leading is not used, so this is the same as TG4. If we start + // using leading, this will fail. + Layout l = b.build(); + assertVertMetrics(l, fmi.top - fmi.ascent, fmi.bottom - fmi.descent, + fmi.top, fmi.descent, + fmi.ascent, fmi.descent, + fmi.ascent, fmi.bottom); + } + + /** + * Basic test showing effect of includePad = true, spacingAdd = 2, wrapping + * to 3 lines. + */ + @SmallTest + public void testGetters6() { + int spacingAdd = 2; // int so expressions return int + LayoutBuilder b = builder() + .setText("This is a longer test") + .setIncludePad(true) + .setWidth(50) + .setSpacingAdd(spacingAdd); + FontMetricsInt fmi = b.paint.getFontMetricsInt(); + + Layout l = b.build(); + assertVertMetrics(l, fmi.top - fmi.ascent, fmi.bottom - fmi.descent, + fmi.top, fmi.descent + spacingAdd, + fmi.ascent, fmi.descent + spacingAdd, + fmi.ascent, fmi.bottom + spacingAdd); + } + + /** + * Basic test showing effect of includePad = true, spacingAdd = 2, + * spacingMult = 1.5, wrapping to 3 lines. + */ + @SmallTest + public void testGetters7() { + LayoutBuilder b = builder() + .setText("This is a longer test") + .setIncludePad(true) + .setWidth(50) + .setSpacingAdd(2) + .setSpacingMult(1.5f); + FontMetricsInt fmi = b.paint.getFontMetricsInt(); + Scaler s = new Scaler(b.spacingMult, b.spacingAdd); + + Layout l = b.build(); + assertVertMetrics(l, fmi.top - fmi.ascent, fmi.bottom - fmi.descent, + fmi.top, fmi.descent + s.scale(fmi.descent - fmi.top), + fmi.ascent, fmi.descent + s.scale(fmi.descent - fmi.ascent), + fmi.ascent, fmi.bottom + s.scale(fmi.bottom - fmi.ascent)); + } + + /** + * Basic test showing effect of includePad = true, spacingAdd = 0, + * spacingMult = 0.8 when wrapping to 3 lines. + */ + @SmallTest + public void testGetters8() { + LayoutBuilder b = builder() + .setText("This is a longer test") + .setIncludePad(true) + .setWidth(50) + .setSpacingAdd(2) + .setSpacingMult(.8f); + FontMetricsInt fmi = b.paint.getFontMetricsInt(); + Scaler s = new Scaler(b.spacingMult, b.spacingAdd); + + Layout l = b.build(); + assertVertMetrics(l, fmi.top - fmi.ascent, fmi.bottom - fmi.descent, + fmi.top, fmi.descent + s.scale(fmi.descent - fmi.top), + fmi.ascent, fmi.descent + s.scale(fmi.descent - fmi.ascent), + fmi.ascent, fmi.bottom + s.scale(fmi.bottom - fmi.ascent)); + } + + // ----- test utility classes and methods ----- + + // Models the effect of the scale and add parameters. I think the current + // implementation misbehaves. + private static class Scaler { + private final float sMult; + private final float sAdd; + + Scaler(float sMult, float sAdd) { + this.sMult = sMult - 1; + this.sAdd = sAdd; + } + + public int scale(float height) { + int altVal = (int)(height * sMult + sAdd + 0.5); // existing impl + int rndVal = Math.round(height * sMult + sAdd); + if (altVal != rndVal) { + Log.i("Scale", "expected scale: " + rndVal + + " != returned scale: " + altVal); + } + return altVal; // existing implementation + } + } + + private static LayoutBuilder builder() { + return new LayoutBuilder(); + } + + private static class LayoutBuilder { + String text = "This is a test"; + TextPaint paint = new TextPaint(); // default + int width = 100; + Alignment align = ALIGN_NORMAL; + float spacingMult = 1; + float spacingAdd = 0; + boolean includePad = false; + + LayoutBuilder setText(String text) { + this.text = text; + return this; + } + + LayoutBuilder setPaint(TextPaint paint) { + this.paint = paint; + return this; + } + + LayoutBuilder setWidth(int width) { + this.width = width; + return this; + } + + LayoutBuilder setAlignment(Alignment align) { + this.align = align; + return this; + } + + LayoutBuilder setSpacingMult(float spacingMult) { + this.spacingMult = spacingMult; + return this; + } + + LayoutBuilder setSpacingAdd(float spacingAdd) { + this.spacingAdd = spacingAdd; + return this; + } + + LayoutBuilder setIncludePad(boolean includePad) { + this.includePad = includePad; + return this; + } + + Layout build() { + return new StaticLayout(text, paint, width, align, spacingMult, + spacingAdd, includePad); + } + } + + private void assertVertMetrics(Layout l, int topPad, int botPad, int... values) { + assertTopBotPadding(l, topPad, botPad); + assertLinesMetrics(l, values); + } + + private void assertLinesMetrics(Layout l, int... values) { + // sanity check + if ((values.length & 0x1) != 0) { + throw new IllegalArgumentException(String.valueOf(values.length)); + } + + int lines = values.length >> 1; + assertEquals(lines, l.getLineCount()); + + int t = 0; + for (int i = 0, n = 0; i < lines; ++i, n += 2) { + int a = values[n]; + int d = values[n+1]; + int h = -a + d; + assertLineMetrics(l, i, t, a, d, h); + t += h; + } + + assertEquals(t, l.getHeight()); + } + + private void assertLineMetrics(Layout l, int line, + int top, int ascent, int descent, int height) { + String info = "line " + line; + assertEquals(info, top, l.getLineTop(line)); + assertEquals(info, ascent, l.getLineAscent(line)); + assertEquals(info, descent, l.getLineDescent(line)); + assertEquals(info, height, l.getLineBottom(line) - top); + } + + private void assertTopBotPadding(Layout l, int topPad, int botPad) { + assertEquals(topPad, l.getTopPadding()); + assertEquals(botPad, l.getBottomPadding()); + } +}