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 com.android.compatibility.common.util.WidgetTestUtils.sameCharSequence;
20 
21 import static org.junit.Assert.assertEquals;
22 import static org.junit.Assert.assertFalse;
23 import static org.junit.Assert.assertNotEquals;
24 import static org.junit.Assert.assertNotNull;
25 import static org.junit.Assert.assertNull;
26 import static org.junit.Assert.assertSame;
27 import static org.junit.Assert.assertTrue;
28 import static org.mockito.Matchers.any;
29 import static org.mockito.Matchers.anyFloat;
30 import static org.mockito.Matchers.anyInt;
31 import static org.mockito.Matchers.eq;
32 import static org.mockito.Mockito.reset;
33 import static org.mockito.Mockito.spy;
34 import static org.mockito.Mockito.times;
35 import static org.mockito.Mockito.verify;
36 
37 import android.graphics.Bitmap;
38 import android.graphics.Bitmap.Config;
39 import android.graphics.Canvas;
40 import android.graphics.Paint;
41 import android.os.LocaleList;
42 import android.text.BoringLayout;
43 import android.text.BoringLayout.Metrics;
44 import android.text.Layout;
45 import android.text.Layout.Alignment;
46 import android.text.PrecomputedText;
47 import android.text.TextDirectionHeuristic;
48 import android.text.TextDirectionHeuristics;
49 import android.text.TextPaint;
50 import android.text.TextUtils;
51 
52 import androidx.test.filters.SmallTest;
53 import androidx.test.runner.AndroidJUnit4;
54 
55 import org.junit.Before;
56 import org.junit.Test;
57 import org.junit.runner.RunWith;
58 
59 @SmallTest
60 @RunWith(AndroidJUnit4.class)
61 public class BoringLayoutTest {
62     private static final float SPACING_MULT_NO_SCALE = 1.0f;
63     private static final float SPACING_ADD_NO_SCALE = 0.0f;
64     private static final int DEFAULT_OUTER_WIDTH = 100;
65     private static final int METRICS_TOP = 10;
66     private static final int METRICS_ASCENT = 20;
67     private static final int METRICS_DESCENT = 40;
68     private static final int METRICS_BOTTOM = 50;
69     private static final int METRICS_WIDTH = 50;
70     private static final int METRICS_LEADING = 50;
71 
72     private static final CharSequence DEFAULT_CHAR_SEQUENCE = "default";
73     private static final TextPaint DEFAULT_PAINT = new TextPaint();
74     private static final Layout.Alignment DEFAULT_ALIGN = Layout.Alignment.ALIGN_CENTER;
75     private static final Metrics DEFAULT_METRICS = createMetrics(
76             METRICS_TOP,
77             METRICS_ASCENT,
78             METRICS_DESCENT,
79             METRICS_BOTTOM,
80             METRICS_WIDTH,
81             METRICS_LEADING);
82 
83     private BoringLayout mBoringLayout;
84 
85     @Before
setup()86     public void setup() {
87         mBoringLayout = makeDefaultBoringLayout();
88     }
89 
90     @Test
testConstructors()91     public void testConstructors() {
92         new BoringLayout(DEFAULT_CHAR_SEQUENCE,
93                 DEFAULT_PAINT,
94                 DEFAULT_OUTER_WIDTH,
95                 DEFAULT_ALIGN,
96                 SPACING_MULT_NO_SCALE,
97                 SPACING_ADD_NO_SCALE,
98                 DEFAULT_METRICS,
99                 true);
100 
101         new BoringLayout(DEFAULT_CHAR_SEQUENCE,
102                 DEFAULT_PAINT,
103                 DEFAULT_OUTER_WIDTH,
104                 DEFAULT_ALIGN,
105                 SPACING_MULT_NO_SCALE,
106                 SPACING_ADD_NO_SCALE,
107                 DEFAULT_METRICS,
108                 true,
109                 TextUtils.TruncateAt.START,
110                 DEFAULT_OUTER_WIDTH);
111     }
112 
verifyMultAddScale(float spacingMult, float spacingAdd)113     private void verifyMultAddScale(float spacingMult, float spacingAdd) {
114         final int height = METRICS_BOTTOM - METRICS_TOP;
115 
116         BoringLayout boringLayout = makeBoringLayout(spacingMult, spacingAdd);
117         assertEquals(height, boringLayout.getHeight());
118         assertEquals(height + METRICS_TOP, boringLayout.getLineDescent(0));
119     }
120 
121     @Test
testScale()122     public void testScale() {
123         // no scale
124         verifyMultAddScale(1.0f, 0.0f);
125 
126         // test line spacing multiplier
127         verifyMultAddScale(2.0f, 0.0f);
128         verifyMultAddScale(0.5f, 0.0f);
129 
130         // test line spacing add
131         verifyMultAddScale(1.0f, 1.5f);
132         verifyMultAddScale(1.0f, -1.6f);
133         verifyMultAddScale(1.0f, 1.4f);
134         verifyMultAddScale(1.0f, -1.4f);
135         verifyMultAddScale(1.0f, 3.0f);
136         verifyMultAddScale(1.0f, -3.0f);
137     }
138 
139     @Test
testPreconditions()140     public void testPreconditions() {
141         assertEquals(1, mBoringLayout.getLineCount());
142         assertEquals(0, mBoringLayout.getLineTop(0));
143         assertEquals(mBoringLayout.getHeight(), mBoringLayout.getLineTop(1));
144         assertEquals(mBoringLayout.getHeight(), mBoringLayout.getLineTop(10));
145         assertEquals(0, mBoringLayout.getLineStart(0));
146         assertEquals(DEFAULT_CHAR_SEQUENCE.length(), mBoringLayout.getLineStart(1));
147         assertEquals(DEFAULT_CHAR_SEQUENCE.length(), mBoringLayout.getLineStart(10));
148         assertEquals(Layout.DIR_LEFT_TO_RIGHT, mBoringLayout.getParagraphDirection(0));
149         assertFalse(mBoringLayout.getLineContainsTab(0));
150         assertEquals((float) METRICS_WIDTH, mBoringLayout.getLineMax(0), 0.0f);
151         assertEquals(Layout.DIR_LEFT_TO_RIGHT, mBoringLayout.getParagraphDirection(0));
152         assertEquals(0, mBoringLayout.getEllipsisCount(0));
153         mBoringLayout.ellipsized(0, 1);
154         assertEquals(1, mBoringLayout.getEllipsisCount(0));
155         mBoringLayout.ellipsized(1, 2);
156         assertEquals(1, mBoringLayout.getEllipsisStart(0));
157     }
158 
159     @Test
testReplaceOrMake()160     public void testReplaceOrMake() {
161         String source = "This is a SpannableString.";
162         BoringLayout layout_1 = mBoringLayout.replaceOrMake(
163                 source,
164                 DEFAULT_PAINT,
165                 DEFAULT_OUTER_WIDTH,
166                 DEFAULT_ALIGN,
167                 SPACING_MULT_NO_SCALE,
168                 SPACING_ADD_NO_SCALE,
169                 DEFAULT_METRICS,
170                 true);
171         assertSame(mBoringLayout, layout_1);
172 
173         layout_1 = mBoringLayout.replaceOrMake(
174                 source,
175                 DEFAULT_PAINT,
176                 DEFAULT_OUTER_WIDTH,
177                 DEFAULT_ALIGN,
178                 SPACING_MULT_NO_SCALE,
179                 SPACING_ADD_NO_SCALE,
180                 DEFAULT_METRICS,
181                 true,
182                 TextUtils.TruncateAt.START,
183                 100);
184         assertSame(mBoringLayout, layout_1);
185         assertEquals(100, mBoringLayout.getEllipsizedWidth());
186     }
187 
188 
189     @Test
testAlignment()190     public void testAlignment() {
191         BoringLayout boringLayout = makeBoringLayoutAlign(Layout.Alignment.ALIGN_NORMAL);
192         assertEquals(0.0f, boringLayout.getLineLeft(0), 0.0f);
193         assertEquals((float) DEFAULT_METRICS.width, boringLayout.getLineRight(0), 0.0f);
194 
195         boringLayout = makeBoringLayoutAlign(Layout.Alignment.ALIGN_CENTER);
196         int expectedWidth = DEFAULT_OUTER_WIDTH - METRICS_WIDTH;
197         assertEquals((float) expectedWidth / 2, boringLayout.getLineLeft(0), 0.0f);
198         expectedWidth = DEFAULT_OUTER_WIDTH + METRICS_WIDTH;
199         assertEquals((float) expectedWidth / 2, boringLayout.getLineRight(0), 0.0f);
200 
201         boringLayout = makeBoringLayoutAlign(Layout.Alignment.ALIGN_OPPOSITE);
202         expectedWidth = DEFAULT_OUTER_WIDTH - METRICS_WIDTH;
203         assertEquals((float) expectedWidth, boringLayout.getLineLeft(0), 0.0f);
204         assertEquals((float) DEFAULT_OUTER_WIDTH, boringLayout.getLineRight(0), 0.0f);
205     }
206 
207     @Test
testGetLineDescent_withIncludePadding()208     public void testGetLineDescent_withIncludePadding() {
209         final int height = METRICS_BOTTOM - METRICS_TOP;
210         assertEquals(height + METRICS_TOP, mBoringLayout.getLineDescent(0));
211     }
212 
213     @Test
testGetLineDescent_withoutIncludePadding()214     public void testGetLineDescent_withoutIncludePadding() {
215         BoringLayout boringLayout = new BoringLayout(
216                 DEFAULT_CHAR_SEQUENCE,
217                 DEFAULT_PAINT,
218                 DEFAULT_OUTER_WIDTH,
219                 DEFAULT_ALIGN,
220                 SPACING_MULT_NO_SCALE,
221                 SPACING_ADD_NO_SCALE,
222                 DEFAULT_METRICS,
223                 false);
224 
225         final int height = METRICS_DESCENT - METRICS_ASCENT;
226         assertEquals(height + METRICS_ASCENT, boringLayout.getLineDescent(0));
227     }
228 
229     @Test
testIncludePadding()230     public void testIncludePadding() {
231         assertEquals(METRICS_TOP - METRICS_ASCENT, mBoringLayout.getTopPadding());
232         assertEquals(METRICS_BOTTOM - METRICS_DESCENT, mBoringLayout.getBottomPadding());
233         assertEquals(METRICS_BOTTOM - METRICS_TOP, mBoringLayout.getHeight());
234 
235         BoringLayout boringLayout = new BoringLayout(
236                 DEFAULT_CHAR_SEQUENCE,
237                 DEFAULT_PAINT,
238                 DEFAULT_OUTER_WIDTH,
239                 DEFAULT_ALIGN,
240                 SPACING_MULT_NO_SCALE,
241                 SPACING_ADD_NO_SCALE,
242                 DEFAULT_METRICS,
243                 false);
244 
245         assertEquals(0, boringLayout.getTopPadding());
246         assertEquals(0, boringLayout.getBottomPadding());
247         assertEquals(METRICS_DESCENT - METRICS_ASCENT, boringLayout.getHeight());
248     }
249 
250     @Test
testIsBoringString()251     public void testIsBoringString() {
252         TextPaint paint = new TextPaint();
253         assertNotNull(BoringLayout.isBoring("hello android", paint));
254 
255         Metrics metrics = new Metrics();
256         metrics.width = 100;
257         assertNotNull(BoringLayout.isBoring("hello android", paint, metrics));
258 
259         assertNull(BoringLayout.isBoring("\u0590 \u0591", paint));
260         assertNull(BoringLayout.isBoring("hello \t android", paint));
261         assertNull(BoringLayout.isBoring("hello \n android", paint));
262         assertNull(BoringLayout.isBoring("hello \n\n\n android", paint));
263         assertNull(BoringLayout.isBoring("\nhello \n android\n", paint));
264         assertNull(BoringLayout.isBoring("hello android\n\n\n", paint));
265     }
266 
267     @Test
testIsBoringForEmptyString()268     public void testIsBoringForEmptyString() {
269         Metrics metrics = new Metrics();
270         assertNotNull(BoringLayout.isBoring("", new TextPaint(), metrics));
271 
272         // The default font Roboto has non-zero ascent/descent values. If metrics returns zeros, it
273         // means failed to retrieve the font metrics.
274         assertNotEquals(0, metrics.ascent);
275         assertNotEquals(0, metrics.descent);
276     }
277 
278     @Test
testIsBoring_resetsFontMetrics()279     public void testIsBoring_resetsFontMetrics() {
280         int someInt = 100;
281         String text = "some text";
282 
283         TextPaint paint = new TextPaint();
284         Paint.FontMetricsInt paintMetrics = paint.getFontMetricsInt();
285         Metrics changedMetrics = new Metrics();
286         changedMetrics.top = paintMetrics.top - someInt;
287         changedMetrics.ascent = paintMetrics.ascent - someInt;
288         changedMetrics.bottom = paintMetrics.bottom + someInt;
289         changedMetrics.descent = paintMetrics.descent + someInt;
290         changedMetrics.leading = paintMetrics.leading + someInt;
291 
292         Metrics expectedMetrics = BoringLayout.isBoring(text, paint, (Metrics) null);
293         Metrics actualMetrics = BoringLayout.isBoring(text, paint, changedMetrics);
294 
295         assertNotNull(actualMetrics);
296         assertNotNull(expectedMetrics);
297         assertEquals(expectedMetrics.top, actualMetrics.top);
298         assertEquals(expectedMetrics.ascent, actualMetrics.ascent);
299         assertEquals(expectedMetrics.bottom, actualMetrics.bottom);
300         assertEquals(expectedMetrics.descent, actualMetrics.descent);
301         assertEquals(expectedMetrics.leading, actualMetrics.leading);
302     }
303 
304     @Test
testGetLineDirections()305     public void testGetLineDirections() {
306         assertNotNull(mBoringLayout.getLineDirections(0));
307         assertNotNull(mBoringLayout.getLineDirections(2));
308     }
309 
310     @Test
testMake()311     public void testMake() {
312         BoringLayout boringLayout = BoringLayout.make(DEFAULT_CHAR_SEQUENCE,
313                 DEFAULT_PAINT,
314                 DEFAULT_OUTER_WIDTH,
315                 DEFAULT_ALIGN,
316                 SPACING_MULT_NO_SCALE,
317                 SPACING_ADD_NO_SCALE,
318                 DEFAULT_METRICS,
319                 true);
320         assertNotNull(boringLayout);
321 
322         boringLayout = BoringLayout.make(DEFAULT_CHAR_SEQUENCE,
323                 DEFAULT_PAINT,
324                 DEFAULT_OUTER_WIDTH,
325                 DEFAULT_ALIGN,
326                 SPACING_MULT_NO_SCALE,
327                 SPACING_ADD_NO_SCALE,
328                 DEFAULT_METRICS,
329                 true,
330                 TextUtils.TruncateAt.START,
331                 DEFAULT_OUTER_WIDTH);
332         assertNotNull(boringLayout);
333     }
334 
335     @Test
testDraw()336     public void testDraw() {
337         BoringLayout boringLayout = BoringLayout.make(DEFAULT_CHAR_SEQUENCE,
338                 DEFAULT_PAINT,
339                 DEFAULT_OUTER_WIDTH,
340                 Alignment.ALIGN_NORMAL,
341                 SPACING_MULT_NO_SCALE,
342                 SPACING_ADD_NO_SCALE,
343                 DEFAULT_METRICS,
344                 true);
345 
346         Bitmap mutableBitmap = Bitmap.createBitmap(10, 28, Config.ARGB_8888);
347         Canvas canvas = spy(new Canvas(mutableBitmap));
348         boringLayout.draw(canvas, null, null, 0);
349         verify(canvas, times(1)).drawText(eq(DEFAULT_CHAR_SEQUENCE.toString()),
350                 anyFloat(), anyFloat(), any(Paint.class));
351 
352         reset(canvas);
353         boringLayout = BoringLayout.make(DEFAULT_CHAR_SEQUENCE,
354                 DEFAULT_PAINT,
355                 DEFAULT_OUTER_WIDTH,
356                 Alignment.ALIGN_OPPOSITE,
357                 SPACING_MULT_NO_SCALE,
358                 SPACING_ADD_NO_SCALE,
359                 DEFAULT_METRICS,
360                 true);
361         boringLayout.draw(canvas, null, null, 0);
362         verify(canvas, times(1)).drawText(sameCharSequence(DEFAULT_CHAR_SEQUENCE),
363                 anyInt(), anyInt(), anyFloat(), anyFloat(), any(Paint.class));
364     }
365 
drawToBitmap(Layout l)366     private static Bitmap drawToBitmap(Layout l) {
367         final Bitmap bmp = Bitmap.createBitmap(l.getWidth(), l.getHeight(), Bitmap.Config.RGB_565);
368         final Canvas c = new Canvas(bmp);
369 
370         c.save();
371         c.translate(0, 0);
372         l.draw(c);
373         c.restore();
374         return bmp;
375     }
376 
textPaintToString(TextPaint p)377     private static String textPaintToString(TextPaint p) {
378         return "{"
379             + "mTextSize=" + p.getTextSize() + ", "
380             + "mTextSkewX=" + p.getTextSkewX() + ", "
381             + "mTextScaleX=" + p.getTextScaleX() + ", "
382             + "mLetterSpacing=" + p.getLetterSpacing() + ", "
383             + "mFlags=" + p.getFlags() + ", "
384             + "mTextLocales=" + p.getTextLocales() + ", "
385             + "mFontVariationSettings=" + p.getFontVariationSettings() + ", "
386             + "mTypeface=" + p.getTypeface() + ", "
387             + "mFontFeatureSettings=" + p.getFontFeatureSettings()
388             + "}";
389     }
390 
directionToString(TextDirectionHeuristic dir)391     private static String directionToString(TextDirectionHeuristic dir) {
392         if (dir == TextDirectionHeuristics.LTR) {
393             return "LTR";
394         } else if (dir == TextDirectionHeuristics.RTL) {
395             return "RTL";
396         } else if (dir == TextDirectionHeuristics.FIRSTSTRONG_LTR) {
397             return "FIRSTSTRONG_LTR";
398         } else if (dir == TextDirectionHeuristics.FIRSTSTRONG_RTL) {
399             return "FIRSTSTRONG_RTL";
400         } else if (dir == TextDirectionHeuristics.ANYRTL_LTR) {
401             return "ANYRTL_LTR";
402         } else {
403             throw new RuntimeException("Unknown Direction");
404         }
405     }
406 
407     static class LayoutParam {
408         final int mStrategy;
409         final int mFrequency;
410         final TextPaint mPaint;
411         final TextDirectionHeuristic mDir;
412 
LayoutParam(int strategy, int frequency, TextPaint paint, TextDirectionHeuristic dir)413         LayoutParam(int strategy, int frequency, TextPaint paint, TextDirectionHeuristic dir) {
414             mStrategy = strategy;
415             mFrequency = frequency;
416             mPaint = new TextPaint(paint);
417             mDir = dir;
418         }
419 
420         @Override
toString()421         public String toString() {
422             return "{"
423                 + "mStrategy=" + mStrategy + ", "
424                 + "mFrequency=" + mFrequency + ", "
425                 + "mPaint=" + textPaintToString(mPaint) + ", "
426                 + "mDir=" + directionToString(mDir)
427                 + "}";
428 
429         }
430 
getPrecomputedText(CharSequence text)431         PrecomputedText getPrecomputedText(CharSequence text) {
432             PrecomputedText.Params param = new PrecomputedText.Params.Builder(mPaint)
433                     .setBreakStrategy(mStrategy)
434                     .setHyphenationFrequency(mFrequency)
435                     .setTextDirection(mDir).build();
436             return PrecomputedText.create(text, param);
437         }
438     };
439 
assertSameDrawOutput( CharSequence text, LayoutParam measuredTextParam, TextPaint layoutPaint)440     void assertSameDrawOutput(
441             CharSequence text,
442             LayoutParam measuredTextParam,
443             TextPaint layoutPaint) {
444         String msg = "BoringLayout#draw for String and PrecomputedText with " + measuredTextParam
445                 + " must output the same BMP.";
446 
447         final BoringLayout.Metrics metricsWithString = BoringLayout.isBoring(text, layoutPaint);
448         final Layout layoutWithString = BoringLayout.make(
449                 text, layoutPaint, metricsWithString.width, Alignment.ALIGN_NORMAL,
450                 SPACING_MULT_NO_SCALE, SPACING_ADD_NO_SCALE, DEFAULT_METRICS,
451                 true /* include padding */);
452 
453         final PrecomputedText precomputed = measuredTextParam.getPrecomputedText(text);
454         final BoringLayout.Metrics metricsWithPrecomputed = BoringLayout.isBoring(
455                 precomputed, layoutPaint);
456         final Layout layoutWithPrecomputed = BoringLayout.make(
457                 precomputed, layoutPaint, metricsWithPrecomputed.width, Alignment.ALIGN_NORMAL,
458                 SPACING_MULT_NO_SCALE, SPACING_ADD_NO_SCALE, DEFAULT_METRICS,
459                 true /* include padding */);
460 
461         assertEquals(msg, layoutWithString.getHeight(), layoutWithPrecomputed.getHeight(), 0.0f);
462 
463         final Bitmap expectedBMP = drawToBitmap(layoutWithString);
464         final Bitmap resultBMP = drawToBitmap(layoutWithPrecomputed);
465 
466         assertTrue(msg, resultBMP.sameAs(expectedBMP));
467     }
468 
469     @Test
testPrecomputedText()470     public void testPrecomputedText() {
471         int[] breaks = {
472             Layout.BREAK_STRATEGY_SIMPLE,
473             Layout.BREAK_STRATEGY_HIGH_QUALITY,
474             Layout.BREAK_STRATEGY_BALANCED,
475         };
476 
477         int[] frequencies = {
478             Layout.HYPHENATION_FREQUENCY_NORMAL,
479             Layout.HYPHENATION_FREQUENCY_FULL,
480             Layout.HYPHENATION_FREQUENCY_NONE,
481         };
482 
483         TextDirectionHeuristic[] dirs = {
484             TextDirectionHeuristics.LTR,
485             TextDirectionHeuristics.RTL,
486             TextDirectionHeuristics.FIRSTSTRONG_LTR,
487             TextDirectionHeuristics.FIRSTSTRONG_RTL,
488             TextDirectionHeuristics.ANYRTL_LTR,
489         };
490 
491         float[] textSizes = {
492             8.0f, 16.0f, 32.0f
493         };
494 
495         LocaleList[] locales = {
496             LocaleList.forLanguageTags("en-US"),
497             LocaleList.forLanguageTags("ja-JP"),
498             LocaleList.forLanguageTags("en-US,ja-JP"),
499         };
500 
501         String onelineText = "Hello, World.";
502 
503         TextPaint paint = new TextPaint();
504 
505         // If the PrecomputedText is created with the same Paint, the draw result must be the same.
506         for (int b : breaks) {
507             for (int f : frequencies) {
508                 for (TextDirectionHeuristic dir : dirs) {
509                     for (float textSize : textSizes) {
510                         for (LocaleList locale : locales) {
511                             paint.setTextSize(textSize);
512                             paint.setTextLocales(locale);
513 
514                             assertSameDrawOutput(onelineText, new LayoutParam(b, f, paint, dir),
515                                     paint);
516                         }
517                     }
518                 }
519             }
520         }
521 
522         // Even if the parameters are different, the output of the static layout must be
523         // same bitmap.
524         for (int bi = 0; bi < breaks.length; bi++) {
525             for (int fi = 0; fi < frequencies.length; fi++) {
526                 for (int diri = 0; diri < dirs.length; diri++) {
527                     for (int sizei = 0; sizei < textSizes.length; sizei++) {
528                         for (int localei = 0; localei < locales.length; localei++) {
529                             TextPaint precomputedPaint = new TextPaint();
530                             TextPaint layoutPaint = new TextPaint();
531 
532                             precomputedPaint.setTextSize(textSizes[sizei]);
533                             layoutPaint.setTextSize(textSizes[(sizei + 1) % textSizes.length]);
534 
535                             precomputedPaint.setTextLocales(locales[localei]);
536                             layoutPaint.setTextLocales(locales[(localei + 1) % locales.length]);
537 
538                             int b = breaks[bi];
539 
540                             int f = frequencies[fi];
541 
542                             TextDirectionHeuristic dir = dirs[diri];
543 
544                             assertSameDrawOutput(onelineText,
545                                     new LayoutParam(b, f, precomputedPaint, dir),
546                                     layoutPaint);
547                         }
548                     }
549                 }
550             }
551         }
552     }
553 
createMetrics( final int top, final int ascent, final int descent, final int bottom, final int width, final int leading)554     private static Metrics createMetrics(
555             final int top,
556             final int ascent,
557             final int descent,
558             final int bottom,
559             final int width,
560             final int leading) {
561 
562         final Metrics metrics = new Metrics();
563 
564         metrics.top = top;
565         metrics.ascent = ascent;
566         metrics.descent = descent;
567         metrics.bottom = bottom;
568         metrics.width = width;
569         metrics.leading = leading;
570 
571         return metrics;
572     }
573 
makeDefaultBoringLayout()574     private static BoringLayout makeDefaultBoringLayout() {
575         return new BoringLayout(DEFAULT_CHAR_SEQUENCE,
576                                 DEFAULT_PAINT,
577                                 DEFAULT_OUTER_WIDTH,
578                                 DEFAULT_ALIGN,
579                                 SPACING_MULT_NO_SCALE,
580                                 SPACING_ADD_NO_SCALE,
581                                 DEFAULT_METRICS,
582                                 true);
583     }
584 
makeBoringLayout(float spacingMult,float spacingAdd)585     private static BoringLayout makeBoringLayout(float spacingMult,float spacingAdd) {
586         return new BoringLayout(DEFAULT_CHAR_SEQUENCE,
587                                 DEFAULT_PAINT,
588                                 DEFAULT_OUTER_WIDTH,
589                                 DEFAULT_ALIGN,
590                                 spacingMult,
591                                 spacingAdd,
592                                 DEFAULT_METRICS,
593                                 true);
594     }
595 
makeBoringLayoutAlign(Alignment align)596     private static BoringLayout makeBoringLayoutAlign(Alignment align) {
597         return new BoringLayout(DEFAULT_CHAR_SEQUENCE,
598                                 DEFAULT_PAINT,
599                                 DEFAULT_OUTER_WIDTH,
600                                 align,
601                                 SPACING_MULT_NO_SCALE,
602                                 SPACING_ADD_NO_SCALE,
603                                 DEFAULT_METRICS,
604                                 true);
605     }
606 }
607