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.graphics.cts;
18 
19 import static org.junit.Assert.assertEquals;
20 import static org.junit.Assert.assertFalse;
21 import static org.junit.Assert.assertNotNull;
22 import static org.junit.Assert.assertNull;
23 import static org.junit.Assert.assertTrue;
24 
25 import android.content.Context;
26 import android.graphics.Bitmap;
27 import android.graphics.BitmapShader;
28 import android.graphics.ColorFilter;
29 import android.graphics.MaskFilter;
30 import android.graphics.Matrix;
31 import android.graphics.Paint;
32 import android.graphics.Paint.Align;
33 import android.graphics.Paint.Cap;
34 import android.graphics.Paint.Join;
35 import android.graphics.Paint.Style;
36 import android.graphics.Path;
37 import android.graphics.PathEffect;
38 import android.graphics.Rect;
39 import android.graphics.Shader;
40 import android.graphics.Typeface;
41 import android.graphics.Xfermode;
42 import android.graphics.fonts.FontVariationAxis;
43 import android.os.LocaleList;
44 import android.support.test.InstrumentationRegistry;
45 import android.support.test.filters.SmallTest;
46 import android.support.test.runner.AndroidJUnit4;
47 import android.text.SpannedString;
48 
49 import org.junit.Test;
50 import org.junit.runner.RunWith;
51 
52 import java.util.Locale;
53 
54 @SmallTest
55 @RunWith(AndroidJUnit4.class)
56 public class PaintTest {
57     private static final Typeface[] TYPEFACES = new Typeface[] {
58             Typeface.DEFAULT,
59             Typeface.DEFAULT_BOLD,
60             Typeface.MONOSPACE,
61             Typeface.SANS_SERIF,
62             Typeface.SERIF,
63     };
64 
65     @Test
testConstructor()66     public void testConstructor() {
67         new Paint();
68 
69         new Paint(1);
70 
71         Paint p = new Paint();
72         new Paint(p);
73     }
74 
75     @Test
testBreakText()76     public void testBreakText() {
77         String text = "HIJKLMN";
78         char[] textChars = text.toCharArray();
79         SpannedString textSpan = new SpannedString(text);
80 
81         Paint p = new Paint();
82 
83         // We need to turn off kerning in order to get accurate comparisons
84         p.setFlags(p.getFlags() & ~Paint.DEV_KERN_TEXT_FLAG);
85 
86         float[] widths = new float[text.length()];
87         assertEquals(text.length(), p.getTextWidths(text, widths));
88 
89         float totalWidth = 0.0f;
90         for (int i = 0; i < text.length(); i++) {
91             totalWidth += widths[i];
92         }
93 
94         for (int i = 0; i < text.length(); i++) {
95             verifyBreakText(text, textChars, textSpan, i, i + 1, true, totalWidth, 1, widths[i]);
96         }
97 
98         // Measure empty string
99         verifyBreakText(text, textChars, textSpan, 0, 0, true, totalWidth, 0, 0);
100 
101         // Measure substring from front: "HIJ"
102         verifyBreakText(text, textChars, textSpan, 0, 3, true, totalWidth,
103                 3, widths[0] + widths[1] + widths[2]);
104 
105         // Reverse measure substring from front: "HIJ"
106         verifyBreakText(text, textChars, textSpan, 0, 3, false, totalWidth,
107                 3, widths[0] + widths[1] + widths[2]);
108 
109         // Measure substring from back: "MN"
110         verifyBreakText(text, textChars, textSpan, 5, 7, true, totalWidth,
111                 2, widths[5] + widths[6]);
112 
113         // Reverse measure substring from back: "MN"
114         verifyBreakText(text, textChars, textSpan, 5, 7, false, totalWidth,
115                 2, widths[5] + widths[6]);
116 
117         // Measure substring in the middle: "JKL"
118         verifyBreakText(text, textChars, textSpan, 2, 5, true, totalWidth,
119                 3, widths[2] + widths[3] + widths[4]);
120 
121         // Reverse measure substring in the middle: "JKL"
122         verifyBreakText(text, textChars, textSpan, 2, 5, false, totalWidth,
123                 3, widths[2] + widths[3] + widths[4]);
124 
125         // Measure substring in the middle and restrict width to the first 2 characters.
126         verifyBreakText(text, textChars, textSpan, 2, 5, true, widths[2] + widths[3],
127                 2, widths[2] + widths[3]);
128 
129         // Reverse measure substring in the middle and restrict width to the last 2 characters.
130         verifyBreakText(text, textChars, textSpan, 2, 5, false, widths[3] + widths[4],
131                 2, widths[3] + widths[4]);
132 
133         // a single Emoji (U+1f601)
134         String emoji = "\ud83d\ude01";
135         char[] emojiChars = emoji.toCharArray();
136         SpannedString emojiSpan = new SpannedString(emoji);
137 
138         float[] emojiWidths = new float[emoji.length()];
139         assertEquals(emoji.length(), p.getTextWidths(emoji, emojiWidths));
140 
141         // Measure substring with a cluster
142         verifyBreakText(emoji, emojiChars, emojiSpan, 0, 2, true, 0,
143                 0, 0);
144 
145         // Measure substring with a cluster
146         verifyBreakText(emoji, emojiChars, emojiSpan, 0, 2, true, emojiWidths[0],
147                 2, emojiWidths[0]);
148 
149         // Reverse measure substring with a cluster
150         verifyBreakText(emoji, emojiChars, emojiSpan, 0, 2, false, 0,
151                 0, 0);
152 
153         // Measure substring with a cluster
154         verifyBreakText(emoji, emojiChars, emojiSpan, 0, 2, false, emojiWidths[0],
155                 2, emojiWidths[0]);
156     }
157 
verifyBreakText(String text, char[] textChars, SpannedString textSpan, int start, int end, boolean measureForwards, float maxWidth, int expectedCount, float expectedWidth)158     private void verifyBreakText(String text, char[] textChars, SpannedString textSpan,
159             int start, int end, boolean measureForwards, float maxWidth, int expectedCount,
160             float expectedWidth) {
161         Paint p = new Paint();
162 
163         // We need to turn off kerning in order to get accurate comparisons
164         p.setFlags(p.getFlags() & ~Paint.DEV_KERN_TEXT_FLAG);
165 
166         int count = end - start;
167         if (!measureForwards) {
168             count = -count;
169         }
170 
171         float[][] measured = new float[][] {
172             new float[1],
173             new float[1],
174             new float[1]
175         };
176         String textSlice = text.substring(start, end);
177         assertEquals(expectedCount, p.breakText(textSlice, measureForwards, maxWidth, measured[0]));
178         assertEquals(expectedCount, p.breakText(textChars, start, count, maxWidth, measured[1]));
179         assertEquals(expectedCount, p.breakText(textSpan, start, end, measureForwards, maxWidth,
180                 measured[2]));
181 
182         for (int i = 0; i < measured.length; i++) {
183             assertEquals("i: " + i, expectedWidth, measured[i][0], 0.0f);
184         }
185     }
186 
187     @Test
testSet()188     public void testSet() {
189         Paint p  = new Paint();
190         Paint p2 = new Paint();
191         ColorFilter c = new ColorFilter();
192         MaskFilter m  = new MaskFilter();
193         PathEffect e  = new PathEffect();
194         Shader s      = new Shader();
195         Typeface t    = Typeface.DEFAULT;
196         Xfermode x = new Xfermode();
197 
198         p.setColorFilter(c);
199         p.setMaskFilter(m);
200         p.setPathEffect(e);
201         p.setShader(s);
202         p.setTypeface(t);
203         p.setXfermode(x);
204         p2.set(p);
205         assertEquals(c, p2.getColorFilter());
206         assertEquals(m, p2.getMaskFilter());
207         assertEquals(e, p2.getPathEffect());
208         assertEquals(s, p2.getShader());
209         assertEquals(t, p2.getTypeface());
210         assertEquals(x, p2.getXfermode());
211 
212         p2.set(p2);
213         assertEquals(c, p2.getColorFilter());
214         assertEquals(m, p2.getMaskFilter());
215         assertEquals(e, p2.getPathEffect());
216         assertEquals(s, p2.getShader());
217         assertEquals(t, p2.getTypeface());
218         assertEquals(x, p2.getXfermode());
219 
220         p.setColorFilter(null);
221         p.setMaskFilter(null);
222         p.setPathEffect(null);
223         p.setShader(null);
224         p.setTypeface(null);
225         p.setXfermode(null);
226         p2.set(p);
227         assertNull(p2.getColorFilter());
228         assertNull(p2.getMaskFilter());
229         assertNull(p2.getPathEffect());
230         assertNull(p2.getShader());
231         assertNull(p2.getTypeface());
232         assertNull(p2.getXfermode());
233 
234         p2.set(p2);
235         assertNull(p2.getColorFilter());
236         assertNull(p2.getMaskFilter());
237         assertNull(p2.getPathEffect());
238         assertNull(p2.getShader());
239         assertNull(p2.getTypeface());
240         assertNull(p2.getXfermode());
241     }
242 
243     @Test
testAccessStrokeCap()244     public void testAccessStrokeCap() {
245         Paint p = new Paint();
246 
247         p.setStrokeCap(Cap.BUTT);
248         assertEquals(Cap.BUTT, p.getStrokeCap());
249 
250         p.setStrokeCap(Cap.ROUND);
251         assertEquals(Cap.ROUND, p.getStrokeCap());
252 
253         p.setStrokeCap(Cap.SQUARE);
254         assertEquals(Cap.SQUARE, p.getStrokeCap());
255     }
256 
257     @Test(expected=RuntimeException.class)
testSetStrokeCapNull()258     public void testSetStrokeCapNull() {
259         Paint p = new Paint();
260 
261         p.setStrokeCap(null);
262     }
263 
264     @Test
testAccessXfermode()265     public void testAccessXfermode() {
266         Paint p = new Paint();
267         Xfermode x = new Xfermode();
268 
269         assertEquals(x, p.setXfermode(x));
270         assertEquals(x, p.getXfermode());
271 
272         assertNull(p.setXfermode(null));
273         assertNull(p.getXfermode());
274     }
275 
276     @Test
testAccessShader()277     public void testAccessShader() {
278         Paint p = new Paint();
279         Shader s = new Shader();
280 
281         assertEquals(s, p.setShader(s));
282         assertEquals(s, p.getShader());
283 
284         assertNull(p.setShader(null));
285         assertNull(p.getShader());
286     }
287 
288     @Test
testShaderLocalMatrix()289     public void testShaderLocalMatrix() {
290         int width = 80;
291         int height = 120;
292         int[] color = new int[width * height];
293         Bitmap bitmap = Bitmap.createBitmap(color, width, height, Bitmap.Config.RGB_565);
294 
295         Paint p = new Paint();
296         Matrix m = new Matrix();
297         Shader s = new BitmapShader(bitmap, Shader.TileMode.REPEAT, Shader.TileMode.REPEAT);
298 
299         // set the shaders matrix to a non identity value and attach to paint
300         m.setScale(10, 0);
301         s.setLocalMatrix(m);
302         p.setShader(s);
303 
304         Matrix m2 = new Matrix();
305         assertTrue(p.getShader().getLocalMatrix(m2));
306         assertEquals(m, m2);
307 
308         // updated the matrix again and set it on the shader but NOT the paint
309         m.setScale(0, 10);
310         s.setLocalMatrix(m);
311 
312         // assert that the matrix on the paint's shader also changed
313         Matrix m3 = new Matrix();
314         assertTrue(p.getShader().getLocalMatrix(m3));
315         assertEquals(m, m3);
316     }
317 
318     @Test
testSetAntiAlias()319     public void testSetAntiAlias() {
320         Paint p = new Paint();
321 
322         p.setAntiAlias(true);
323         assertTrue(p.isAntiAlias());
324 
325         p.setAntiAlias(false);
326         assertFalse(p.isAntiAlias());
327     }
328 
329     @Test
testAccessTypeface()330     public void testAccessTypeface() {
331         Paint p = new Paint();
332 
333         assertEquals(Typeface.DEFAULT, p.setTypeface(Typeface.DEFAULT));
334         assertEquals(Typeface.DEFAULT, p.getTypeface());
335 
336         assertEquals(Typeface.DEFAULT_BOLD, p.setTypeface(Typeface.DEFAULT_BOLD));
337         assertEquals(Typeface.DEFAULT_BOLD, p.getTypeface());
338 
339         assertEquals(Typeface.MONOSPACE, p.setTypeface(Typeface.MONOSPACE));
340         assertEquals(Typeface.MONOSPACE, p.getTypeface());
341 
342         assertNull(p.setTypeface(null));
343         assertNull(p.getTypeface());
344     }
345 
346     @Test
testAccessPathEffect()347     public void testAccessPathEffect() {
348         Paint p = new Paint();
349         PathEffect e = new PathEffect();
350 
351         assertEquals(e, p.setPathEffect(e));
352         assertEquals(e, p.getPathEffect());
353 
354         assertNull(p.setPathEffect(null));
355         assertNull(p.getPathEffect());
356     }
357 
358     @Test
testSetFakeBoldText()359     public void testSetFakeBoldText() {
360         Paint p = new Paint();
361 
362         p.setFakeBoldText(true);
363         assertTrue(p.isFakeBoldText());
364 
365         p.setFakeBoldText(false);
366         assertFalse(p.isFakeBoldText());
367     }
368 
369     @Test
testAccessStrokeJoin()370     public void testAccessStrokeJoin() {
371         Paint p = new Paint();
372 
373         p.setStrokeJoin(Join.BEVEL);
374         assertEquals(Join.BEVEL, p.getStrokeJoin());
375 
376         p.setStrokeJoin(Join.MITER);
377         assertEquals(Join.MITER, p.getStrokeJoin());
378 
379         p.setStrokeJoin(Join.ROUND);
380         assertEquals(Join.ROUND, p.getStrokeJoin());
381     }
382 
383     @Test(expected=RuntimeException.class)
testSetStrokeJoinNull()384     public void testSetStrokeJoinNull() {
385         Paint p = new Paint();
386 
387         p.setStrokeJoin(null);
388     }
389 
390     @Test
testAccessStyle()391     public void testAccessStyle() {
392         Paint p = new Paint();
393 
394         p.setStyle(Style.FILL);
395         assertEquals(Style.FILL, p.getStyle());
396 
397         p.setStyle(Style.FILL_AND_STROKE);
398         assertEquals(Style.FILL_AND_STROKE, p.getStyle());
399 
400         p.setStyle(Style.STROKE);
401         assertEquals(Style.STROKE, p.getStyle());
402     }
403 
404     @Test(expected=RuntimeException.class)
testSetStyleNull()405     public void testSetStyleNull() {
406         Paint p = new Paint();
407 
408         p.setStyle(null);
409     }
410 
411     @Test
testGetFontSpacing()412     public void testGetFontSpacing() {
413         Paint p = new Paint();
414 
415         for (Typeface typeface : TYPEFACES) {
416             p.setTypeface(typeface);
417 
418             p.setTextSize(10);
419             float spacing10 = p.getFontSpacing();
420             assertTrue(spacing10 > 0);
421 
422             p.setTextSize(20);
423             float spacing20 = p.getFontSpacing();
424             assertTrue(spacing20 > spacing10);
425         }
426     }
427 
428     @Test
testSetSubpixelText()429     public void testSetSubpixelText() {
430         Paint p = new Paint();
431 
432         p.setSubpixelText(true);
433         assertTrue(p.isSubpixelText());
434 
435         p.setSubpixelText(false);
436         assertFalse(p.isSubpixelText());
437     }
438 
439     @Test
testAccessTextScaleX()440     public void testAccessTextScaleX() {
441         Paint p = new Paint();
442 
443         p.setTextScaleX(2.0f);
444         assertEquals(2.0f, p.getTextScaleX(), 0.0f);
445 
446         p.setTextScaleX(1.0f);
447         assertEquals(1.0f, p.getTextScaleX(), 0.0f);
448 
449         p.setTextScaleX(0.0f);
450         assertEquals(0.0f, p.getTextScaleX(), 0.0f);
451 
452     }
453 
454     @Test
testAccessMaskFilter()455     public void testAccessMaskFilter() {
456         Paint p = new Paint();
457         MaskFilter m = new MaskFilter();
458 
459         assertEquals(m, p.setMaskFilter(m));
460         assertEquals(m, p.getMaskFilter());
461 
462         assertNull(p.setMaskFilter(null));
463         assertNull(p.getMaskFilter());
464     }
465 
466     @Test
testAccessColorFilter()467     public void testAccessColorFilter() {
468         Paint p = new Paint();
469         ColorFilter c = new ColorFilter();
470 
471         assertEquals(c, p.setColorFilter(c));
472         assertEquals(c, p.getColorFilter());
473 
474         assertNull(p.setColorFilter(null));
475         assertNull(p.getColorFilter());
476     }
477 
478     @Test
testSetARGB()479     public void testSetARGB() {
480         Paint p = new Paint();
481 
482         p.setARGB(0, 0, 0, 0);
483         assertEquals(0, p.getColor());
484 
485         p.setARGB(3, 3, 3, 3);
486         assertEquals((3 << 24) | (3 << 16) | (3 << 8) | 3, p.getColor());
487     }
488 
489     @Test
testAscent()490     public void testAscent() {
491         Paint p = new Paint();
492 
493         for (Typeface typeface : TYPEFACES) {
494             p.setTypeface(typeface);
495 
496             p.setTextSize(10);
497             float ascent10 = p.ascent();
498             assertTrue(ascent10 < 0);
499 
500             p.setTextSize(20);
501             float ascent20 = p.ascent();
502             assertTrue(ascent20 < ascent10);
503         }
504     }
505 
506     @Test
507     public void testAccessTextSkewX() {
508         Paint p = new Paint();
509 
510         p.setTextSkewX(1.0f);
511         assertEquals(1.0f, p.getTextSkewX(), 0.0f);
512 
513         p.setTextSkewX(0.0f);
514         assertEquals(0.0f, p.getTextSkewX(), 0.0f);
515 
516         p.setTextSkewX(-0.25f);
517         assertEquals(-0.25f, p.getTextSkewX(), 0.0f);
518     }
519 
520     @Test
521     public void testAccessTextSize() {
522         Paint p = new Paint();
523 
524         p.setTextSize(1.0f);
525         assertEquals(1.0f, p.getTextSize(), 0.0f);
526 
527         p.setTextSize(2.0f);
528         assertEquals(2.0f, p.getTextSize(), 0.0f);
529 
530         // text size should be greater than 0, so set -1 has no effect
531         p.setTextSize(-1.0f);
532         assertEquals(2.0f, p.getTextSize(), 0.0f);
533 
534         // text size should be greater than or equals to 0
535         p.setTextSize(0.0f);
536         assertEquals(0.0f, p.getTextSize(), 0.0f);
537     }
538 
539     @Test
540     public void testGetTextWidths() throws Exception {
541         String text = "HIJKLMN";
542         char[] textChars = text.toCharArray();
543         SpannedString textSpan = new SpannedString(text);
544 
545         // Test measuring the widths of the entire text
546         verifyGetTextWidths(text, textChars, textSpan, 0, 7);
547 
548         // Test measuring a substring of the text
549         verifyGetTextWidths(text, textChars, textSpan, 1, 3);
550 
551         // Test measuring a substring of zero length.
552         verifyGetTextWidths(text, textChars, textSpan, 3, 3);
553 
554         // Test measuring substrings from the front and back
555         verifyGetTextWidths(text, textChars, textSpan, 0, 2);
556         verifyGetTextWidths(text, textChars, textSpan, 4, 7);
557     }
558 
559     /** Tests all four overloads of getTextWidths are the same. */
560     private void verifyGetTextWidths(String text, char[] textChars, SpannedString textSpan,
561             int start, int end) {
562         Paint p = new Paint();
563         int count = end - start;
564         float[][] widths = new float[][] {
565             new float[count],
566             new float[count],
567             new float[count],
568             new float[count]
569         };
570 
571         String textSlice = text.substring(start, end);
572         assertEquals(count, p.getTextWidths(textSlice, widths[0]));
573         assertEquals(count, p.getTextWidths(textChars, start, count, widths[1]));
574         assertEquals(count, p.getTextWidths(textSpan, start, end, widths[2]));
575         assertEquals(count, p.getTextWidths(text, start, end, widths[3]));
576 
577         // Check that the widths returned by the overloads are the same.
578         for (int i = 0; i < count; i++) {
579             assertEquals(widths[0][i], widths[1][i], 0.0f);
580             assertEquals(widths[1][i], widths[2][i], 0.0f);
581             assertEquals(widths[2][i], widths[3][i], 0.0f);
582         }
583     }
584 
585     @Test
586     public void testSetStrikeThruText() {
587         Paint p = new Paint();
588 
589         p.setStrikeThruText(true);
590         assertTrue(p.isStrikeThruText());
591 
592         p.setStrikeThruText(false);
593         assertFalse(p.isStrikeThruText());
594     }
595 
596     @Test
597     public void testAccessTextAlign() {
598         Paint p = new Paint();
599 
600         p.setTextAlign(Align.CENTER);
601         assertEquals(Align.CENTER, p.getTextAlign());
602 
603         p.setTextAlign(Align.LEFT);
604         assertEquals(Align.LEFT, p.getTextAlign());
605 
606         p.setTextAlign(Align.RIGHT);
607         assertEquals(Align.RIGHT, p.getTextAlign());
608     }
609 
610     @Test
611     public void testAccessTextLocale() {
612         Paint p = new Paint();
613 
614         final Locale defaultLocale = Locale.getDefault();
615 
616         // Check default
617         assertEquals(defaultLocale, p.getTextLocale());
618 
619         // Check setter / getters
620         p.setTextLocale(Locale.US);
621         assertEquals(Locale.US, p.getTextLocale());
622         assertEquals(new LocaleList(Locale.US), p.getTextLocales());
623 
624         p.setTextLocale(Locale.CHINESE);
625         assertEquals(Locale.CHINESE, p.getTextLocale());
626         assertEquals(new LocaleList(Locale.CHINESE), p.getTextLocales());
627 
628         p.setTextLocale(Locale.JAPANESE);
629         assertEquals(Locale.JAPANESE, p.getTextLocale());
630         assertEquals(new LocaleList(Locale.JAPANESE), p.getTextLocales());
631 
632         p.setTextLocale(Locale.KOREAN);
633         assertEquals(Locale.KOREAN, p.getTextLocale());
634         assertEquals(new LocaleList(Locale.KOREAN), p.getTextLocales());
635 
636         // Check reverting back to default
637         p.setTextLocale(defaultLocale);
638         assertEquals(defaultLocale, p.getTextLocale());
639         assertEquals(new LocaleList(defaultLocale), p.getTextLocales());
640     }
641 
642     @Test(expected=IllegalArgumentException.class)
643     public void testSetTextLocaleNull() {
644         Paint p = new Paint();
645 
646         p.setTextLocale(null);
647     }
648 
649     @Test
650     public void testAccessTextLocales() {
651         Paint p = new Paint();
652 
653         final LocaleList defaultLocales = LocaleList.getDefault();
654 
655         // Check default
656         assertEquals(defaultLocales, p.getTextLocales());
657 
658         // Check setter / getters for a one-member locale list
659         p.setTextLocales(new LocaleList(Locale.CHINESE));
660         assertEquals(Locale.CHINESE, p.getTextLocale());
661         assertEquals(new LocaleList(Locale.CHINESE), p.getTextLocales());
662 
663         // Check setter / getters for a two-member locale list
664         p.setTextLocales(LocaleList.forLanguageTags("fr,de"));
665         assertEquals(Locale.forLanguageTag("fr"), p.getTextLocale());
666         assertEquals(LocaleList.forLanguageTags("fr,de"), p.getTextLocales());
667 
668         // Check reverting back to default
669         p.setTextLocales(defaultLocales);
670         assertEquals(defaultLocales, p.getTextLocales());
671     }
672 
673     @Test(expected=IllegalArgumentException.class)
674     public void testAccessTextLocalesNull() {
675         Paint p = new Paint();
676 
677         // Check that we cannot pass a null locale list
678         p.setTextLocales(null);
679     }
680 
681     @Test(expected=IllegalArgumentException.class)
682     public void testAccessTextLocalesEmpty() {
683         Paint p = new Paint();
684 
685         // Check that we cannot pass an empty locale list
686         p.setTextLocales(new LocaleList());
687     }
688 
689     @Test
690     public void testGetFillPath() {
691         Paint p = new Paint();
692         Path path1 = new Path();
693         Path path2 = new Path();
694 
695         assertTrue(path1.isEmpty());
696         assertTrue(path2.isEmpty());
697         p.getFillPath(path1, path2);
698         assertTrue(path1.isEmpty());
699         assertTrue(path2.isEmpty());
700 
701         // No setter
702     }
703 
704     @Test
705     public void testAccessAlpha() {
706         Paint p = new Paint();
707 
708         p.setAlpha(0);
709         assertEquals(0, p.getAlpha());
710 
711         p.setAlpha(255);
712         assertEquals(255, p.getAlpha());
713 
714         // set value should between 0 and 255, so 266 is rounded to 10
715         p.setAlpha(266);
716         assertEquals(10, p.getAlpha());
717 
718         // set value should between 0 and 255, so -20 is rounded to 236
719         p.setAlpha(-20);
720         assertEquals(236, p.getAlpha());
721     }
722 
723     @Test
724     public void testSetFilterBitmap() {
725         Paint p = new Paint();
726 
727         p.setFilterBitmap(true);
728         assertTrue(p.isFilterBitmap());
729 
730         p.setFilterBitmap(false);
731         assertFalse(p.isFilterBitmap());
732     }
733 
734     @Test
735     public void testAccessColor() {
736         Paint p = new Paint();
737 
738         p.setColor(1);
739         assertEquals(1, p.getColor());
740 
741         p.setColor(0);
742         assertEquals(0, p.getColor());
743 
744         p.setColor(255);
745         assertEquals(255, p.getColor());
746 
747         p.setColor(-1);
748         assertEquals(-1, p.getColor());
749 
750         p.setColor(256);
751         assertEquals(256, p.getColor());
752     }
753 
754     @Test
755     public void testSetShadowLayer() {
756         new Paint().setShadowLayer(10, 1, 1, 0);
757     }
758 
759     @Test
760     public void testGetFontMetrics1() {
761         Paint p = new Paint();
762         Paint.FontMetrics fm = new Paint.FontMetrics();
763 
764         for (Typeface typeface : TYPEFACES) {
765             p.setTypeface(typeface);
766 
767             p.setTextSize(10);
768             p.getFontMetrics(fm);
769             assertEquals(p.ascent(), fm.ascent, 0.0f);
770             assertEquals(p.descent(), fm.descent, 0.0f);
771 
772             p.setTextSize(20);
773             p.getFontMetrics(fm);
774             assertEquals(p.ascent(), fm.ascent, 0.0f);
775             assertEquals(p.descent(), fm.descent, 0.0f);
776         }
777     }
778 
779     @Test
780     public void testGetFontMetrics2() {
781         Paint p = new Paint();
782 
783         for (Typeface typeface : TYPEFACES) {
784             p.setTypeface(typeface);
785 
786             p.setTextSize(10);
787             Paint.FontMetrics fm = p.getFontMetrics();
788             assertEquals(p.ascent(), fm.ascent, 0.0f);
789             assertEquals(p.descent(), fm.descent, 0.0f);
790 
791             p.setTextSize(20);
792             fm = p.getFontMetrics();
793             assertEquals(p.ascent(), fm.ascent, 0.0f);
794             assertEquals(p.descent(), fm.descent, 0.0f);
795         }
796     }
797 
798     @Test
799     public void testAccessStrokeMiter() {
800         Paint p = new Paint();
801 
802         p.setStrokeMiter(0.0f);
803         assertEquals(0.0f, p.getStrokeMiter(), 0.0f);
804 
805         p.setStrokeMiter(10.0f);
806         assertEquals(10.0f, p.getStrokeMiter(), 0.0f);
807 
808         // set value should be greater or equal to 0, set to -10.0f has no effect
809         p.setStrokeMiter(-10.0f);
810         assertEquals(10.0f, p.getStrokeMiter(), 0.0f);
811     }
812 
813     @Test
814     public void testClearShadowLayer() {
815         new Paint().clearShadowLayer();
816     }
817 
818     @Test
819     public void testSetUnderlineText() {
820         Paint p = new Paint();
821 
822         p.setUnderlineText(true);
823         assertTrue(p.isUnderlineText());
824 
825         p.setUnderlineText(false);
826         assertFalse(p.isUnderlineText());
827     }
828 
829     @Test
830     public void testSetDither() {
831         Paint p = new Paint();
832 
833         p.setDither(true);
834         assertTrue(p.isDither());
835 
836         p.setDither(false);
837         assertFalse(p.isDither());
838     }
839 
840     @Test
841     public void testDescent() {
842         Paint p = new Paint();
843 
844         for (Typeface typeface : TYPEFACES) {
845             p.setTypeface(typeface);
846 
847             p.setTextSize(10);
848             float descent10 = p.descent();
849             assertTrue(descent10 > 0);
850 
851             p.setTextSize(20);
852             float descent20 = p.descent();
853             assertTrue(descent20 > descent10);
854         }
855     }
856 
857     @Test
858     public void testAccessFlags() {
859         Paint p = new Paint();
860 
861         p.setFlags(Paint.ANTI_ALIAS_FLAG);
862         assertEquals(Paint.ANTI_ALIAS_FLAG, p.getFlags());
863 
864         p.setFlags(Paint.DEV_KERN_TEXT_FLAG);
865         assertEquals(Paint.DEV_KERN_TEXT_FLAG, p.getFlags());
866     }
867 
868     @Test
869     public void testAccessStrokeWidth() {
870         Paint p = new Paint();
871 
872         p.setStrokeWidth(0.0f);
873         assertEquals(0.0f, p.getStrokeWidth(), 0.0f);
874 
875         p.setStrokeWidth(10.0f);
876         assertEquals(10.0f, p.getStrokeWidth(), 0.0f);
877 
878         // set value must greater or equal to 0, set -10.0f has no effect
879         p.setStrokeWidth(-10.0f);
880         assertEquals(10.0f, p.getStrokeWidth(), 0.0f);
881     }
882 
883     @Test
884     public void testSetFontFeatureSettings() {
885         Paint p = new Paint();
886         // Roboto font (system default) has "fi" ligature
887         String text = "fi";
888         float[] widths = new float[text.length()];
889         p.getTextWidths(text, widths);
890         assertTrue(widths[0] > 0.0f);
891         assertEquals(0.0f, widths[1], 0.0f);
892 
893         // Disable ligature using OpenType feature
894         p.setFontFeatureSettings("'liga' off");
895         p.getTextWidths(text, widths);
896         assertTrue(widths[0] > 0.0f);
897         assertTrue(widths[1] > 0.0f);
898 
899         // Re-enable ligature
900         p.setFontFeatureSettings("'liga' on");
901         p.getTextWidths(text, widths);
902         assertTrue(widths[0] > 0.0f);
903         assertEquals(0.0f, widths[1], 0.0f);
904     }
905 
906     @Test
testSetFontVariationSettings_defaultTypeface()907     public void testSetFontVariationSettings_defaultTypeface() {
908         new Paint().setFontVariationSettings("'wght' 400");
909     }
910 
911     @Test
testSetGetFontVariationSettings()912     public void testSetGetFontVariationSettings() {
913         final Paint defaultPaint = new Paint();
914 
915         Paint p = new Paint();
916         Context context = InstrumentationRegistry.getTargetContext();
917         Typeface typeface = Typeface.createFromAsset(context.getAssets(), "multiaxis.ttf");
918         p.setTypeface(typeface);
919 
920         // multiaxis.ttf supports "wght", "PRIV", "PR12" axes.
921 
922         // The default variation settings should be null.
923         assertNull(p.getFontVariationSettings());
924 
925         final String[] nonEffectiveSettings = {
926                 "'slnt' 30",  // unsupported tag
927                 "'BBBB' 1.0",  // unsupported tag
928                 "'A   ' 1.0",  // unsupported tag
929                 "'PR0 ' 1.3",  // unsupported tag
930                 "'WGHT' 0.7",  // unsupported tag (case sensitive)
931                 "'BBBB' 1.0, 'CCCC' 2.0",  // none of them are supported.
932         };
933 
934         for (String notEffectiveSetting : nonEffectiveSettings) {
935             if (!defaultPaint.setFontVariationSettings(notEffectiveSetting)) {
936                 // Test only when the system font don't support the above axes. OEMs may add
937                 // their fonts and these font may support above axes.
938                 assertFalse("Must return false for " + notEffectiveSetting,
939                         p.setFontVariationSettings(notEffectiveSetting));
940                 assertNull("Must not change settings for " + notEffectiveSetting,
941                         p.getFontVariationSettings());
942             }
943         }
944 
945         String retainSettings = "'wght' 400";
946         assertTrue(p.setFontVariationSettings(retainSettings));
947         for (String notEffectiveSetting : nonEffectiveSettings) {
948             assertFalse(p.setFontVariationSettings(notEffectiveSetting));
949             assertEquals("Must not change settings for " + notEffectiveSetting,
950                     retainSettings, p.getFontVariationSettings());
951         }
952 
953         // At least one axis is supported, the settings should be applied.
954         final String[] effectiveSettings = {
955                 "'wght' 300",  // supported tag
956                 "'wght' 300, 'PRIV' 0.5",  // both are supported
957                 "'PRIV' 1.0, 'BBBB' 0.4",  // 'BBBB' is unsupported
958         };
959 
960         for (String effectiveSetting : effectiveSettings) {
961             assertTrue("Must return true for " + effectiveSetting,
962                     p.setFontVariationSettings(effectiveSetting));
963             assertEquals(effectiveSetting, p.getFontVariationSettings());
964         }
965 
966         p.setFontVariationSettings("");
967         assertNull(p.getFontVariationSettings());
968     }
969 
970     @Test
testGetTextBounds()971     public void testGetTextBounds() {
972         Paint p = new Paint();
973         p.setTextSize(10);
974         String text1 = "hello";
975         Rect bounds1 = new Rect();
976         Rect bounds2 = new Rect();
977         p.getTextBounds(text1, 0, text1.length(), bounds1);
978         char[] textChars1 = text1.toCharArray();
979         p.getTextBounds(textChars1, 0, textChars1.length, bounds2);
980         // verify that string and char array methods produce consistent results
981         assertEquals(bounds1, bounds2);
982         String text2 = "hello world";
983 
984         // verify substring produces consistent results
985         p.getTextBounds(text2, 0, text1.length(), bounds2);
986         assertEquals(bounds1, bounds2);
987 
988         // longer string is expected to have same left edge but be wider
989         p.getTextBounds(text2, 0, text2.length(), bounds2);
990         assertEquals(bounds1.left, bounds2.left);
991         assertTrue(bounds2.right > bounds1.right);
992 
993         // bigger size implies bigger bounding rect
994         p.setTextSize(20);
995         p.getTextBounds(text1, 0, text1.length(), bounds2);
996         assertTrue(bounds2.right > bounds1.right);
997         assertTrue(bounds2.bottom - bounds2.top > bounds1.bottom - bounds1.top);
998     }
999 
1000     @Test
testReset()1001     public void testReset() {
1002         Paint p  = new Paint();
1003         ColorFilter c = new ColorFilter();
1004         MaskFilter m  = new MaskFilter();
1005         PathEffect e  = new PathEffect();
1006         Shader s      = new Shader();
1007         Typeface t    = Typeface.DEFAULT;
1008         Xfermode x = new Xfermode();
1009 
1010         p.setColorFilter(c);
1011         p.setMaskFilter(m);
1012         p.setPathEffect(e);
1013         p.setShader(s);
1014         p.setTypeface(t);
1015         p.setXfermode(x);
1016         p.setFlags(Paint.ANTI_ALIAS_FLAG);
1017         assertEquals(c, p.getColorFilter());
1018         assertEquals(m, p.getMaskFilter());
1019         assertEquals(e, p.getPathEffect());
1020         assertEquals(s, p.getShader());
1021         assertEquals(t, p.getTypeface());
1022         assertEquals(x, p.getXfermode());
1023         assertEquals(Paint.ANTI_ALIAS_FLAG, p.getFlags());
1024 
1025         p.reset();
1026         assertEquals(Paint.DEV_KERN_TEXT_FLAG | Paint.EMBEDDED_BITMAP_TEXT_FLAG, p.getFlags());
1027         assertEquals(null, p.getColorFilter());
1028         assertEquals(null, p.getMaskFilter());
1029         assertEquals(null, p.getPathEffect());
1030         assertEquals(null, p.getShader());
1031         assertEquals(null, p.getTypeface());
1032         assertEquals(null, p.getXfermode());
1033     }
1034 
1035     @Test
testSetLinearText()1036     public void testSetLinearText() {
1037         Paint p = new Paint();
1038 
1039         p.setLinearText(true);
1040         assertTrue(p.isLinearText());
1041 
1042         p.setLinearText(false);
1043         assertFalse(p.isLinearText());
1044     }
1045 
1046     @Test
testGetFontMetricsInt1()1047     public void testGetFontMetricsInt1() {
1048         Paint p = new Paint();
1049         Paint.FontMetricsInt fmi = new Paint.FontMetricsInt();
1050 
1051         for (Typeface typeface : TYPEFACES) {
1052             p.setTypeface(typeface);
1053 
1054             p.setTextSize(10);
1055             p.getFontMetricsInt(fmi);
1056             assertEquals(Math.round(p.ascent()), fmi.ascent);
1057             assertEquals(Math.round(p.descent()), fmi.descent);
1058 
1059             p.setTextSize(20);
1060             p.getFontMetricsInt(fmi);
1061             assertEquals(Math.round(p.ascent()), fmi.ascent);
1062             assertEquals(Math.round(p.descent()), fmi.descent);
1063         }
1064     }
1065 
1066     @Test
testGetFontMetricsInt2()1067     public void testGetFontMetricsInt2() {
1068         Paint p = new Paint();
1069         Paint.FontMetricsInt fmi;
1070 
1071         for (Typeface typeface : TYPEFACES) {
1072             p.setTypeface(typeface);
1073 
1074             p.setTextSize(10);
1075             fmi = p.getFontMetricsInt();
1076             assertEquals(Math.round(p.ascent()), fmi.ascent);
1077             assertEquals(Math.round(p.descent()), fmi.descent);
1078 
1079             p.setTextSize(20);
1080             fmi = p.getFontMetricsInt();
1081             assertEquals(Math.round(p.ascent()), fmi.ascent);
1082             assertEquals(Math.round(p.descent()), fmi.descent);
1083         }
1084     }
1085 
1086     @Test
testMeasureText()1087     public void testMeasureText() {
1088         String text = "HIJKLMN";
1089         char[] textChars = text.toCharArray();
1090         SpannedString textSpan = new SpannedString(text);
1091 
1092         Paint p = new Paint();
1093 
1094         // We need to turn off kerning in order to get accurate comparisons
1095         p.setFlags(p.getFlags() & ~Paint.DEV_KERN_TEXT_FLAG);
1096 
1097         float[] widths = new float[text.length()];
1098         for (int i = 0; i < widths.length; i++) {
1099             widths[i] = p.measureText(text, i, i + 1);
1100         }
1101 
1102         float totalWidth = 0;
1103         for (int i = 0; i < widths.length; i++) {
1104             totalWidth += widths[i];
1105         }
1106 
1107         // Test measuring the widths of the entire text
1108         verifyMeasureText(text, textChars, textSpan, 0, 7, totalWidth);
1109 
1110         // Test measuring a substring of the text
1111         verifyMeasureText(text, textChars, textSpan, 1, 3, widths[1] + widths[2]);
1112 
1113         // Test measuring a substring of zero length.
1114         verifyMeasureText(text, textChars, textSpan, 3, 3, 0);
1115 
1116         // Test measuring substrings from the front and back
1117         verifyMeasureText(text, textChars, textSpan, 0, 2, widths[0] + widths[1]);
1118         verifyMeasureText(text, textChars, textSpan, 4, 7, widths[4] + widths[5] + widths[6]);
1119     }
1120 
1121     @Test
testMeasureTextContext()1122     public void testMeasureTextContext() {
1123        Paint p = new Paint();
1124        // Arabic LAM, which is different width depending on context
1125        String shortString = "\u0644";
1126        String longString = "\u0644\u0644\u0644";
1127        char[] longChars = longString.toCharArray();
1128        SpannedString longSpanned = new SpannedString(longString);
1129        float width = p.measureText(shortString);
1130        // Verify that measurement of substring is consistent no matter what surrounds it.
1131        verifyMeasureText(longString, longChars, longSpanned, 0, 1, width);
1132        verifyMeasureText(longString, longChars, longSpanned, 1, 2, width);
1133        verifyMeasureText(longString, longChars, longSpanned, 2, 3, width);
1134     }
1135 
1136     @Test
testMeasureTextWithLongText()1137     public void testMeasureTextWithLongText() {
1138         final int MAX_COUNT = 65535;
1139         char[] longText = new char[MAX_COUNT];
1140         for (int n = 0; n < MAX_COUNT; n++) {
1141             longText[n] = 'm';
1142         }
1143 
1144         Paint p = new Paint();
1145         float width = p.measureText(longText, 0, 1);
1146         assertEquals(true, width > 0);
1147     }
1148 
1149     /** Tests that all four overloads of measureText are the same and match some value. */
verifyMeasureText(String text, char[] textChars, SpannedString textSpan, int start, int end, float expectedWidth)1150     private void verifyMeasureText(String text, char[] textChars, SpannedString textSpan,
1151             int start, int end, float expectedWidth) {
1152         Paint p = new Paint();
1153 
1154         // We need to turn off kerning in order to get accurate comparisons
1155         p.setFlags(p.getFlags() & ~Paint.DEV_KERN_TEXT_FLAG);
1156 
1157         int count = end - start;
1158         float[] widths = new float[] {-1, -1, -1, -1};
1159 
1160         String textSlice = text.substring(start, end);
1161         widths[0] = p.measureText(textSlice);
1162         widths[1] = p.measureText(textChars, start, count);
1163         widths[2] = p.measureText(textSpan, start, end);
1164         widths[3] = p.measureText(text, start, end);
1165 
1166         // Check that the widths returned by the overloads are the same.
1167         assertEquals(widths[0], widths[1], 0.0f);
1168         assertEquals(widths[1], widths[2], 0.0f);
1169         assertEquals(widths[2], widths[3], 0.0f);
1170         assertEquals(widths[3], expectedWidth, 0.0f);
1171     }
1172 
1173     @Test
testGetTextPathCharArray()1174     public void testGetTextPathCharArray() {
1175         Path path = new Path();
1176 
1177         assertTrue(path.isEmpty());
1178         new Paint().getTextPath(new char[] {'H', 'I', 'J', 'K', 'L', 'M', 'N'}, 0, 7, 0, 0, path);
1179         assertFalse(path.isEmpty());
1180     }
1181 
1182     @Test(expected=RuntimeException.class)
testGetTextPathCharArrayNegativeIndex()1183     public void testGetTextPathCharArrayNegativeIndex() {
1184         new Paint().getTextPath(new char[] {'H', 'I', 'J', 'K', 'L', 'M', 'N'}, -2, 7, 0, 0,
1185                 new Path());
1186     }
1187 
1188     @Test(expected=RuntimeException.class)
testGetTextPathCharArrayNegativeCount()1189     public void testGetTextPathCharArrayNegativeCount() {
1190         new Paint().getTextPath(new char[] {'H', 'I', 'J', 'K', 'L', 'M', 'N'}, 0, -3, 0, 0,
1191                 new Path());
1192     }
1193 
1194     @Test(expected=RuntimeException.class)
testGetTextPathCharArrayCountTooHigh()1195     public void testGetTextPathCharArrayCountTooHigh() {
1196         new Paint().getTextPath(new char[] {'H', 'I', 'J', 'K', 'L', 'M', 'N'}, 3, 7, 0, 0,
1197                 new Path());
1198     }
1199 
1200     @Test
testGetTextPathString()1201     public void testGetTextPathString() {
1202         Path path = new Path();
1203 
1204         assertTrue(path.isEmpty());
1205         new Paint().getTextPath("HIJKLMN", 0, 7, 0, 0, path);
1206         assertFalse(path.isEmpty());
1207     }
1208 
1209     @Test(expected=RuntimeException.class)
testGetTextPathStringNegativeIndex()1210     public void testGetTextPathStringNegativeIndex() {
1211         new Paint().getTextPath("HIJKLMN", -2, 7, 0, 0, new Path());
1212     }
1213 
1214     @Test(expected=RuntimeException.class)
testGetTextPathStringNegativeCount()1215     public void testGetTextPathStringNegativeCount() {
1216         new Paint().getTextPath("HIJKLMN", 0, -3, 0, 0, new Path());
1217     }
1218 
1219     @Test(expected=RuntimeException.class)
testGetTextPathStringStartTooHigh()1220     public void testGetTextPathStringStartTooHigh() {
1221         new Paint().getTextPath("HIJKLMN", 7, 3, 0, 0, new Path());
1222     }
1223 
1224     @Test(expected=RuntimeException.class)
testGetTextPathStringCountTooHigh()1225     public void testGetTextPathStringCountTooHigh() {
1226         new Paint().getTextPath("HIJKLMN", 3, 9, 0, 0, new Path());
1227     }
1228 
1229     @Test
testHasGlyph()1230     public void testHasGlyph() {
1231         Paint p = new Paint();
1232 
1233         // This method tests both the logic of hasGlyph and the sanity of fonts present
1234         // on the device.
1235         assertTrue(p.hasGlyph("A"));
1236         assertFalse(p.hasGlyph("\uFFFE"));  // U+FFFE is guaranteed to be a noncharacter
1237 
1238         // Roboto 2 (the default typeface) does have an "fi" glyph and is mandated by CDD
1239         assertTrue(p.hasGlyph("fi"));
1240         assertFalse(p.hasGlyph("ab"));  // but it does not contain an "ab" glyph
1241         assertTrue(p.hasGlyph("\u02E5\u02E9"));  // IPA tone mark ligature
1242 
1243         // variation selectors
1244         assertFalse(p.hasGlyph("a\uFE0F"));
1245         assertFalse(p.hasGlyph("a\uDB40\uDDEF"));  // UTF-16 encoding of U+E01EF
1246         assertFalse(p.hasGlyph("\u2229\uFE0F"));  // base character is in mathematical symbol font
1247         // Note: U+FE0F is variation selection, unofficially reserved for emoji
1248 
1249         // regional indicator symbols
1250         assertTrue(p.hasGlyph("\uD83C\uDDEF\uD83C\uDDF5"));   // "JP" U+1F1EF U+1F1F5
1251         assertFalse(p.hasGlyph("\uD83C\uDDFF\uD83C\uDDFF"));  // "ZZ" U+1F1FF U+1F1FF
1252 
1253         // Mongolian, which is an optional font, but if present, should support FVS
1254         if (p.hasGlyph("\u182D")) {
1255             assertTrue(p.hasGlyph("\u182D\u180B"));
1256         }
1257 
1258         // Emoji with variation selector support for both text and emoji presentation
1259         assertTrue(p.hasGlyph("\u231A\uFE0E"));  // WATCH + VS15
1260         assertTrue(p.hasGlyph("\u231A\uFE0F"));  // WATCH + VS16
1261 
1262         // Unicode 7.0, 8.0, and 9.0 emoji should be supported.
1263         assertTrue(p.hasGlyph("\uD83D\uDD75"));  // SLEUTH OR SPY is introduced in Unicode 7.0
1264         assertTrue(p.hasGlyph("\uD83C\uDF2E"));  // TACO is introduced in Unicode 8.0
1265         assertTrue(p.hasGlyph("\uD83E\uDD33"));  // SELFIE is introduced in Unicode 9.0
1266 
1267         // We don't require gender-neutral emoji, but if present, results must be consistent
1268         // whether VS is present or not.
1269         assertTrue(p.hasGlyph("\uD83D\uDC69\u200D\u2695") ==  // WOMAN, ZWJ, STAFF OF AESCULAPIUS
1270                 p.hasGlyph("\uD83D\uDC69\u200D\u2695\uFE0F"));  // above + VS16
1271     }
1272 
1273     @Test
testGetRunAdvance()1274     public void testGetRunAdvance() {
1275         Paint p = new Paint();
1276         {
1277             // LTR
1278             String string = "abcdef";
1279             {
1280                 final float width = p.getRunAdvance(string, 0, string.length(), 0,
1281                         string.length(), false, 0);
1282                 assertEquals(0.0f, width, 0.0f);
1283             }
1284             {
1285                 for (int i = 0; i < string.length(); i++) {
1286                     final float width = p.getRunAdvance(string, i, i + 1, 0, string.length(),
1287                             false, i);
1288                     assertEquals(0.0f, width, 0.0f);
1289                 }
1290             }
1291             {
1292                 final float widthToMid = p.getRunAdvance(string, 0, string.length(), 0,
1293                         string.length(), false, string.length() / 2);
1294                 final float widthToTail = p.getRunAdvance(string, 0, string.length(), 0,
1295                         string.length(), false, string.length());
1296                 assertTrue(widthToMid > 0.0f);
1297                 assertTrue(widthToTail > widthToMid);
1298             }
1299             {
1300                 final float widthFromHead = p.getRunAdvance(string, 0, string.length(), 0,
1301                         string.length(), false, string.length());
1302                 final float widthFromSecond = p.getRunAdvance(string, 1, string.length(), 0,
1303                         string.length(), false, string.length());
1304                 assertTrue(widthFromHead > widthFromSecond);
1305             }
1306             {
1307                 float width = 0.0f;
1308                 for (int i = 0; i < string.length(); i++) {
1309                     width += p.getRunAdvance(string, i, i + 1, 0, string.length(), false, i + 1);
1310                 }
1311                 final float totalWidth = p.getRunAdvance(string, 0, string.length(), 0,
1312                         string.length(), false, string.length());
1313                 assertEquals(totalWidth, width, 1.0f);
1314             }
1315         }
1316         {
1317             // RTL
1318             String string = "\u0644\u063A\u0629 \u0639\u0631\u0628\u064A\u0629"; // Arabic
1319             {
1320                 final float width = p.getRunAdvance(string, 0, string.length(), 0,
1321                         string.length(), true, 0);
1322                 assertEquals(0.0f, width, 0.0f);
1323             }
1324             {
1325                 for (int i = 0; i < string.length(); i++) {
1326                     final float width = p.getRunAdvance(string, i, i + 1, 0, string.length(),
1327                             true, i);
1328                     assertEquals(0.0f, width, 0.0f);
1329                 }
1330             }
1331             {
1332                 final float widthToMid = p.getRunAdvance(string, 0, string.length(), 0,
1333                         string.length(), true, string.length() / 2);
1334                 final float widthToTail = p.getRunAdvance(string, 0, string.length(), 0,
1335                         string.length(), true, string.length());
1336                 assertTrue(widthToMid > 0.0f);
1337                 assertTrue(widthToTail > widthToMid);
1338             }
1339             {
1340                 final float widthFromHead = p.getRunAdvance(string, 0, string.length(), 0,
1341                         string.length(), true, string.length());
1342                 final float widthFromSecond = p.getRunAdvance(string, 1, string.length(), 0,
1343                         string.length(), true, string.length());
1344                 assertTrue(widthFromHead > widthFromSecond);
1345             }
1346         }
1347     }
1348 
1349     @Test(expected=IllegalArgumentException.class)
testGetRunAdvanceNullCharSequence()1350     public void testGetRunAdvanceNullCharSequence() {
1351         new Paint().getRunAdvance((CharSequence) null, 0, 0, 0, 0, false, 0);
1352     }
1353 
1354     @Test(expected=IllegalArgumentException.class)
testGetRunAdvanceNullCharArray()1355     public void testGetRunAdvanceNullCharArray() {
1356         new Paint().getRunAdvance((char[]) null, 0, 0, 0, 0, false, 0);
1357     }
1358 
1359     @Test(expected=IndexOutOfBoundsException.class)
testGetRunAdvanceTextLengthLessThenContextEnd()1360     public void testGetRunAdvanceTextLengthLessThenContextEnd() {
1361         final String string = "abcde";
1362 
1363         // text length < context end
1364         new Paint().getRunAdvance(string, 0, string.length(), 0, string.length() + 1, false,
1365                 string.length());
1366     }
1367 
1368     @Test(expected=IndexOutOfBoundsException.class)
testGetRunAdvanceContextEndLessThanEnd()1369     public void testGetRunAdvanceContextEndLessThanEnd() {
1370         final String string = "abcde";
1371 
1372         // context end < end
1373         new Paint().getRunAdvance(string, 0, string.length(), 0, string.length() - 1, false, 0);
1374     }
1375 
1376     @Test(expected=IndexOutOfBoundsException.class)
testGetRunAdvanceEndLessThanOffset()1377     public void testGetRunAdvanceEndLessThanOffset() {
1378         final String string = "abcde";
1379 
1380         // end < offset
1381         new Paint().getRunAdvance(string, 0, string.length() - 1, 0, string.length() - 1, false,
1382                 string.length());
1383     }
1384 
1385     @Test(expected=IndexOutOfBoundsException.class)
testGetRunAdvanceOffsetLessThanStart()1386     public void testGetRunAdvanceOffsetLessThanStart() {
1387         final String string = "abcde";
1388 
1389         // offset < start
1390         new Paint().getRunAdvance(string, 1, string.length(), 1, string.length(), false, 0);
1391     }
1392 
1393     @Test(expected=IndexOutOfBoundsException.class)
testGetRunAdvanceStartLessThanContextStart()1394     public void testGetRunAdvanceStartLessThanContextStart() {
1395         final String string = "abcde";
1396 
1397         // start < context start
1398         new Paint().getRunAdvance(string, 0, string.length(), 1, string.length(), false, 1);
1399     }
1400 
1401     @Test(expected=IndexOutOfBoundsException.class)
testGetRunAdvanceContextStartNegative()1402     public void testGetRunAdvanceContextStartNegative() {
1403         final String string = "abcde";
1404 
1405         // context start < 0
1406         new Paint().getRunAdvance(string, 0, string.length(), -1, string.length(), false, 0);
1407     }
1408 
1409     @Test
testGetRunAdvance_nonzeroIndex()1410     public void testGetRunAdvance_nonzeroIndex() {
1411         Paint p = new Paint();
1412         final String text = "Android powers hundreds of millions of mobile " +
1413                 "devices in more than 190 countries around the world. It's" +
1414                 "the largest installed base of any mobile platform and" +
1415                 "growing fast—every day another million users power up their" +
1416                 "Android devices for the first time and start looking for" +
1417                 "apps, games, and other digital content.";
1418         // Test offset index does not affect width.
1419         final float widthAndroidFirst = p.getRunAdvance(
1420                 text, 0, 7, 0, text.length(), false, 7);
1421         final float widthAndroidSecond = p.getRunAdvance(
1422                 text, 215, 222, 0, text.length(), false, 222);
1423         assertTrue(Math.abs(widthAndroidFirst - widthAndroidSecond) < 1);
1424     }
1425 
1426     @Test
1427     public void testGetRunAdvance_glyphDependingContext() {
1428         Paint p = new Paint();
1429         // Test the context change the character shape.
1430         // First character should be isolated form because the context ends at index 1.
1431         final float isolatedFormWidth = p.getRunAdvance("\u0644\u0644", 0, 1, 0, 1, true, 1);
1432         // First character should be initial form because the context ends at index 2.
1433         final float initialFormWidth = p.getRunAdvance("\u0644\u0644", 0, 1, 0, 2, true, 1);
1434         assertTrue(isolatedFormWidth > initialFormWidth);
1435     }
1436 
1437     @Test
testGetRunAdvance_arabic()1438     public void testGetRunAdvance_arabic() {
1439         Paint p = new Paint();
1440         // Test total width is equals to sum of each character's width.
1441         // "What is Unicode?" in Arabic.
1442         final String text =
1443                 "\u0645\u0627\u0020\u0647\u064A\u0020\u0627\u0644\u0634" +
1444                 "\u0641\u0631\u0629\u0020\u0627\u0644\u0645\u0648\u062D" +
1445                 "\u062F\u0629\u0020\u064A\u0648\u0646\u064A\u0643\u0648" +
1446                 "\u062F\u061F";
1447         final float totalWidth = p.getRunAdvance(
1448                 text, 0, text.length(), 0, text.length(), true, text.length());
1449         float sumOfCharactersWidth = 0;
1450         for (int i = 0; i < text.length(); i++) {
1451             sumOfCharactersWidth += p.getRunAdvance(
1452                     text, i, i + 1, 0, text.length(), true, i + 1);
1453         }
1454         assertTrue(Math.abs(totalWidth - sumOfCharactersWidth) < 1);
1455     }
1456 
1457     @Test
1458     public void testGetOffsetForAdvance() {
1459         Paint p = new Paint();
1460         {
1461             // LTR
1462             String string = "abcdef";
1463             {
1464                 for (int offset = 0; offset <= string.length(); ++offset) {
1465                     final float widthToOffset = p.getRunAdvance(string, 0,
1466                             string.length(), 0, string.length(), false, offset);
1467                     final int restoredOffset = p.getOffsetForAdvance(string, 0,
1468                             string.length(), 0, string.length(), false, widthToOffset);
1469                     assertEquals(offset, restoredOffset);
1470                 }
1471             }
1472             {
1473                 final int offset = p.getOffsetForAdvance(string, 0, string.length(), 0,
1474                         string.length(), false, -10.0f);
1475                 assertEquals(0, offset);
1476             }
1477             {
1478                 final float widthToEnd = p.getRunAdvance(string, 0, string.length(), 0,
1479                         string.length(), true, string.length());
1480                 final int offset = p.getOffsetForAdvance(string, 0, string.length(), 0,
1481                         string.length(), true, widthToEnd + 10.0f);
1482                 assertEquals(string.length(), offset);
1483             }
1484         }
1485         {
1486             // RTL
1487             String string = "\u0639\u0631\u0628\u0649"; // Arabic
1488             {
1489                 for (int offset = 0; offset <= string.length(); ++offset) {
1490                     final float widthToOffset = p.getRunAdvance(string, 0,
1491                             string.length(), 0, string.length(), true, offset);
1492                     final int restoredOffset = p.getOffsetForAdvance(string, 0,
1493                             string.length(), 0, string.length(), true, widthToOffset);
1494                     assertEquals(offset, restoredOffset);
1495                 }
1496             }
1497             {
1498                 final int offset = p.getOffsetForAdvance(string, 0, string.length(), 0,
1499                         string.length(), true, -10.0f);
1500                 assertEquals(0, offset);
1501             }
1502             {
1503                 final float widthToEnd = p.getRunAdvance(string, 0, string.length(), 0,
1504                         string.length(), true, string.length());
1505                 final int offset = p.getOffsetForAdvance(string, 0, string.length(), 0,
1506                         string.length(), true, widthToEnd + 10.0f);
1507                 assertEquals(string.length(), offset);
1508             }
1509         }
1510     }
1511 
1512     @Test(expected=IllegalArgumentException.class)
1513     public void testGetOffsetForAdvanceNullCharSequence() {
1514         new Paint().getOffsetForAdvance((CharSequence) null, 0, 0, 0, 0, false, 0.0f);
1515     }
1516 
1517     @Test(expected=IllegalArgumentException.class)
1518     public void testGetOffsetForAdvanceNullCharArray() {
1519         new Paint().getOffsetForAdvance((char[]) null, 0, 0, 0, 0, false, 0.0f);
1520     }
1521 
1522     @Test(expected=IndexOutOfBoundsException.class)
1523     public void testGetOffsetForAdvanceContextStartNegative() {
1524         final String string = "abcde";
1525 
1526         // context start < 0
1527         new Paint().getOffsetForAdvance(string, -1, string.length(), 0, string.length(), false,
1528                 0.0f);
1529     }
1530 
1531     @Test(expected=IndexOutOfBoundsException.class)
1532     public void testGetOffsetForAdvanceStartLessThanContextStart() {
1533         final String string = "abcde";
1534 
1535         // start < context start
1536         new Paint().getOffsetForAdvance(string, 0, string.length(), 1, string.length(), false,
1537                 0.0f);
1538     }
1539 
1540     @Test(expected=IndexOutOfBoundsException.class)
1541     public void testGetOffsetForAdvanceEndLessThanStart() {
1542         final String string = "abcde";
1543 
1544         // end < start
1545         new Paint().getOffsetForAdvance(string, 1, 0, 0, 0, false, 0);
1546     }
1547 
1548     @Test(expected=IndexOutOfBoundsException.class)
1549     public void testGetOffsetForAdvanceContextEndLessThanEnd() {
1550         final String string = "abcde";
1551 
1552         // context end < end
1553         new Paint().getOffsetForAdvance(string, 0, string.length(), 0, string.length() - 1, false,
1554                 0.0f);
1555     }
1556 
1557     @Test(expected=IndexOutOfBoundsException.class)
1558     public void testGetOffsetForAdvanceTextLengthLessThanContextEnd() {
1559         final String string = "abcde";
1560 
1561         // text length < context end
1562         new Paint().getOffsetForAdvance(string, 0, string.length(), 0, string.length() + 1, false,
1563                 0.0f);
1564     }
1565 
1566     @Test
1567     public void testGetOffsetForAdvance_graphemeCluster() {
1568         Paint p = new Paint();
1569         {
1570             String string = "\uD83C\uDF37"; // U+1F337: TULIP
1571             {
1572                 final float widthToOffset = p.getRunAdvance(string, 0,
1573                         string.length(), 0, string.length(), false, 1);
1574                 final int offset = p.getOffsetForAdvance(string, 0, string.length(), 0,
1575                         string.length(), false, widthToOffset);
1576                 assertFalse(1 == offset);
1577                 assertTrue(0 == offset || string.length() == offset);
1578             }
1579         }
1580         {
1581             String string = "\uD83C\uDDFA\uD83C\uDDF8"; // US flag
1582             {
1583                 final float widthToOffset = p.getRunAdvance(string, 0,
1584                         string.length(), 0, string.length(), false, 2);
1585                 final int offset = p.getOffsetForAdvance(string, 0, string.length(), 0,
1586                         string.length(), false, widthToOffset);
1587                 assertFalse(2 == offset);
1588                 assertTrue(0 == offset || string.length() == offset);
1589             }
1590             {
1591                 final float widthToOffset = p.getRunAdvance(string, 0, 2, 0, 2, false, 2);
1592                 final int offset = p.getOffsetForAdvance(string, 0, 2,
1593                         0, 2, false, widthToOffset);
1594                 assertEquals(2, offset);
1595             }
1596         }
1597         {
1598             // HANGUL CHOSEONG KIYEOK, HANGUL JUNGSEONG A, HANDUL JONGSEONG KIYEOK
1599             String string = "\u1100\u1161\u11A8";
1600             {
1601                 for (int offset = 0; offset <= string.length(); ++offset) {
1602                     final float widthToOffset = p.getRunAdvance(string, 0,
1603                             string.length(), 0, string.length(), false, offset);
1604                     final int offsetForAdvance = p.getOffsetForAdvance(string, 0, string.length(),
1605                             0, string.length(), false, widthToOffset);
1606                     assertTrue(0 == offsetForAdvance || string.length() == offsetForAdvance);
1607                 }
1608                 for (int offset = 0; offset <= string.length(); ++offset) {
1609                     final float widthToOffset = p.getRunAdvance(string, 0, offset, 0, offset,
1610                             false, offset);
1611                     final int offsetForAdvance = p.getOffsetForAdvance(string, 0, string.length(),
1612                             0, string.length(), false, widthToOffset);
1613                     assertTrue(0 == offsetForAdvance || string.length() == offsetForAdvance);
1614                 }
1615                 for (int offset = 0; offset <= string.length(); ++offset) {
1616                     final float widthToOffset = p.getRunAdvance(string, 0, offset, 0, offset,
1617                             false, offset);
1618                     final int offsetForAdvance = p.getOffsetForAdvance(string, 0, offset, 0,
1619                             offset, false, widthToOffset);
1620                     assertEquals(offset, offsetForAdvance);
1621                 }
1622             }
1623         }
1624     }
1625 
1626     @Test
1627     public void testElegantText() {
1628         final Paint p = new Paint();
1629         p.setTextSize(10);
1630         assertFalse(p.isElegantTextHeight());
1631         final float nonElegantTop = p.getFontMetrics().top;
1632         final float nonElegantBottom = p.getFontMetrics().bottom;
1633 
1634         p.setElegantTextHeight(true);
1635         assertTrue(p.isElegantTextHeight());
1636         final float elegantTop = p.getFontMetrics().top;
1637         final float elegantBottom = p.getFontMetrics().bottom;
1638 
1639         assertTrue(elegantTop < nonElegantTop);
1640         assertTrue(elegantBottom > nonElegantBottom);
1641 
1642         p.setElegantTextHeight(false);
1643         assertFalse(p.isElegantTextHeight());
1644     }
1645 }
1646