• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2018 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.fonts;
18 
19 import static com.google.common.truth.Truth.assertWithMessage;
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.assertTrue;
27 
28 import android.content.Context;
29 import android.content.res.AssetManager;
30 import android.content.res.Resources;
31 import android.content.res.Resources.NotFoundException;
32 import android.graphics.Paint;
33 import android.graphics.RectF;
34 import android.graphics.Typeface;
35 import android.graphics.cts.R;
36 import android.graphics.text.PositionedGlyphs;
37 import android.graphics.text.TextRunShaper;
38 import android.os.ParcelFileDescriptor;
39 import android.util.Log;
40 import android.util.Pair;
41 
42 import androidx.test.InstrumentationRegistry;
43 import androidx.test.filters.SmallTest;
44 import androidx.test.runner.AndroidJUnit4;
45 
46 import org.junit.Test;
47 import org.junit.runner.RunWith;
48 
49 import java.io.File;
50 import java.io.FileInputStream;
51 import java.io.FileOutputStream;
52 import java.io.IOException;
53 import java.io.InputStream;
54 import java.nio.ByteBuffer;
55 import java.nio.channels.FileChannel;
56 import java.util.ArrayList;
57 import java.util.Arrays;
58 import java.util.Comparator;
59 
60 @SmallTest
61 @RunWith(AndroidJUnit4.class)
62 public class FontTest {
63     private static final String TAG = "FontFileUtilTest";
64     private static final String CACHE_FILE_PREFIX = ".font";
65 
66     /**
67      * Create new temporary file.
68      *
69      * Caller must delete the file after used.
70      */
getTempFile()71     private static File getTempFile() {
72         Context ctx = InstrumentationRegistry.getTargetContext();
73         final String prefix = CACHE_FILE_PREFIX;
74         for (int i = 0; i < 100; ++i) {
75             final File file = new File(ctx.getCacheDir(), prefix + i);
76             try {
77                 if (file.createNewFile()) {
78                     return file;
79                 }
80             } catch (IOException e) {
81                 // ignore. Try next file.
82             }
83         }
84         return null;
85     }
86 
mmap(AssetManager am, String path)87     private static ByteBuffer mmap(AssetManager am, String path) {
88         File file = getTempFile();
89         try (InputStream is = am.open(path)) {
90             if (!copyToFile(file, is)) {
91                 return null;
92             }
93             return mmap(file);
94         } catch (IOException e) {
95             Log.e(TAG, "Failed to open assets");
96             return null;
97         } finally {
98             file.delete();
99         }
100     }
101 
mmap(File file)102     private static ByteBuffer mmap(File file) {
103         try (FileInputStream fis = new FileInputStream(file)) {
104             FileChannel channel = fis.getChannel();
105             return channel.map(FileChannel.MapMode.READ_ONLY, 0, channel.size());
106         } catch (IOException e) {
107             return null;
108         }
109     }
110 
copyToFile(File file, InputStream is)111     private static boolean copyToFile(File file, InputStream is) {
112         return copyToFile(file, is, null, null);
113     }
114 
copyToFile(File file, InputStream is, byte[] prepend, byte[] append)115     private static boolean copyToFile(File file, InputStream is, byte[] prepend, byte[] append) {
116         try (FileOutputStream os = new FileOutputStream(file, false)) {
117             byte[] buffer = new byte[1024];
118             int readLen;
119             if (prepend != null) {
120                 os.write(prepend, 0, prepend.length);
121             }
122             while ((readLen = is.read(buffer)) != -1) {
123                 os.write(buffer, 0, readLen);
124             }
125             if (append != null) {
126                 os.write(append, 0, append.length);
127             }
128             return true;
129         } catch (IOException e) {
130             Log.e(TAG, "Error copying resource contents to temp file: " + e.getMessage());
131             return false;
132         }
133     }
134 
openTypeValueFromTag(String tag)135     private int openTypeValueFromTag(String tag) {
136         assertEquals(4, tag.length());
137         return ((int) tag.charAt(0)) << 24 | ((int) tag.charAt(1)) << 16
138                 | ((int) tag.charAt(2)) << 8 | ((int) tag.charAt(3));
139     }
140 
assertAxesEquals(String msg, FontVariationAxis[] left, FontVariationAxis[] right)141     private void assertAxesEquals(String msg, FontVariationAxis[] left, FontVariationAxis[] right) {
142         if (left == right) {
143             return;
144         }
145 
146         if (left == null) {
147             assertWithMessage(msg).that(right).isEmpty();
148         } else if (right == null) {
149             assertWithMessage(msg).that(left).isEmpty();
150         } else {
151             // Sort both font variation settings because the font variation axes are unordered.
152             Comparator<FontVariationAxis> axisComparator =
153                     (l, r) -> openTypeValueFromTag(l.getTag()) - openTypeValueFromTag(r.getTag());
154             Arrays.sort(left, axisComparator);
155             Arrays.sort(right, axisComparator);
156 
157             assertWithMessage(msg).that(left).isEqualTo(right);
158         }
159     }
160 
assertNullOrEmpty(String msg, FontVariationAxis[] actual)161     private void assertNullOrEmpty(String msg, FontVariationAxis[] actual) {
162         assertWithMessage(msg).that(actual == null || actual.length == 0).isTrue();
163     }
164 
165     @Test
testBuilder_buffer()166     public void testBuilder_buffer() throws IOException {
167         AssetManager am = InstrumentationRegistry.getTargetContext().getAssets();
168         for (Pair<Integer, Boolean> style : FontTestUtil.getAllStyles()) {
169             int weight = style.first.intValue();
170             boolean italic = style.second.booleanValue();
171             final int slant = italic ? FontStyle.FONT_SLANT_ITALIC : FontStyle.FONT_SLANT_UPRIGHT;
172             String path = FontTestUtil.getFontPathFromStyle(weight, italic);
173 
174             ByteBuffer buffer = mmap(am, path);
175 
176             Font font = new Font.Builder(buffer).build();
177             assertEquals(path, weight, font.getStyle().getWeight());
178             assertEquals(path, slant, font.getStyle().getSlant());
179             assertEquals(path, 0, font.getTtcIndex());
180             assertNullOrEmpty(path, font.getAxes());
181             assertNotNull(font.getBuffer());
182             assertNull(font.getFile());
183         }
184     }
185 
186     @Test
testBuilder_buffer_ttc()187     public void testBuilder_buffer_ttc() throws IOException {
188         AssetManager am = InstrumentationRegistry.getTargetContext().getAssets();
189         for (Pair<Integer, Boolean> style : FontTestUtil.getAllStyles()) {
190             int weight = style.first.intValue();
191             boolean italic = style.second.booleanValue();
192             final int slant = italic ? FontStyle.FONT_SLANT_ITALIC : FontStyle.FONT_SLANT_UPRIGHT;
193             String path = FontTestUtil.getTtcFontFileInAsset();
194 
195             ByteBuffer buffer = mmap(am, path);
196             int ttcIndex = FontTestUtil.getTtcIndexFromStyle(weight, italic);
197 
198             Font font = new Font.Builder(buffer).setTtcIndex(ttcIndex).build();
199             assertEquals(path, weight, font.getStyle().getWeight());
200             assertEquals(path, slant, font.getStyle().getSlant());
201             assertEquals(path, ttcIndex, font.getTtcIndex());
202             assertNullOrEmpty(path, font.getAxes());
203             assertNotNull(font.getBuffer());
204             assertNull(font.getFile());
205         }
206     }
207 
208     @Test
testBuilder_buffer_vf()209     public void testBuilder_buffer_vf() throws IOException {
210         AssetManager am = InstrumentationRegistry.getTargetContext().getAssets();
211         for (Pair<Integer, Boolean> style : FontTestUtil.getAllStyles()) {
212             int weight = style.first.intValue();
213             boolean italic = style.second.booleanValue();
214             String path = FontTestUtil.getVFFontInAsset();
215             final int slant = italic ? FontStyle.FONT_SLANT_ITALIC : FontStyle.FONT_SLANT_UPRIGHT;
216 
217             ByteBuffer buffer = mmap(am, path);
218             FontVariationAxis[] axes = FontVariationAxis.fromFontVariationSettings(
219                     FontTestUtil.getVarSettingsFromStyle(weight, italic));
220 
221             Font font = new Font.Builder(buffer).setFontVariationSettings(axes).build();
222             assertEquals(path, weight, font.getStyle().getWeight());
223             assertEquals(path, slant, font.getStyle().getSlant());
224             assertEquals(path, 0, font.getTtcIndex());
225             assertAxesEquals(path, axes, font.getAxes());
226             assertNotNull(font.getBuffer());
227             assertNull(font.getFile());
228         }
229     }
230 
231     @Test
testBuilder_buffer_override()232     public void testBuilder_buffer_override() throws IOException {
233         int customWeight = 350;
234         AssetManager am = InstrumentationRegistry.getTargetContext().getAssets();
235         for (Pair<Integer, Boolean> style : FontTestUtil.getAllStyles()) {
236             int weight = style.first.intValue();
237             boolean italic = style.second.booleanValue();
238             String path = FontTestUtil.getFontPathFromStyle(weight, italic);
239             final int slant = italic ? FontStyle.FONT_SLANT_ITALIC : FontStyle.FONT_SLANT_UPRIGHT;
240 
241             ByteBuffer buffer = mmap(am, path);
242 
243             Font font = new Font.Builder(buffer).setWeight(customWeight).build();
244             assertEquals(path, customWeight, font.getStyle().getWeight());
245             assertEquals(path, slant, font.getStyle().getSlant());
246             assertEquals(path, 0, font.getTtcIndex());
247             assertNullOrEmpty(path, font.getAxes());
248             assertNotNull(font.getBuffer());
249             assertNull(font.getFile());
250         }
251 
252         for (Pair<Integer, Boolean> style : FontTestUtil.getAllStyles()) {
253             int weight = style.first.intValue();
254             boolean italic = style.second.booleanValue();
255             String path = FontTestUtil.getFontPathFromStyle(weight, italic);
256 
257             ByteBuffer buffer = mmap(am, path);
258 
259             Font font = new Font.Builder(buffer).setSlant(FontStyle.FONT_SLANT_ITALIC).build();
260             assertEquals(path, weight, font.getStyle().getWeight());
261             assertEquals(path, FontStyle.FONT_SLANT_ITALIC, font.getStyle().getSlant());
262             assertEquals(path, 0, font.getTtcIndex());
263             assertNullOrEmpty(path, font.getAxes());
264             assertNotNull(font.getBuffer());
265             assertNull(font.getFile());
266         }
267     }
268 
269     @Test(expected = NullPointerException.class)
testBuilder_buffer_invalid_null()270     public void testBuilder_buffer_invalid_null() throws IOException {
271         ByteBuffer buf = null;
272         new Font.Builder(buf);
273     }
274 
275     @Test(expected = IllegalArgumentException.class)
testBuilder_buffer_invalid_not_direct()276     public void testBuilder_buffer_invalid_not_direct() throws IOException {
277         ByteBuffer buf = ByteBuffer.allocate(1024);
278         new Font.Builder(buf);
279     }
280 
281     @Test
testBuilder_file()282     public void testBuilder_file() throws IOException {
283         AssetManager am = InstrumentationRegistry.getTargetContext().getAssets();
284         for (Pair<Integer, Boolean> style : FontTestUtil.getAllStyles()) {
285             int weight = style.first.intValue();
286             boolean italic = style.second.booleanValue();
287             String path = FontTestUtil.getFontPathFromStyle(weight, italic);
288             final int slant = italic ? FontStyle.FONT_SLANT_ITALIC : FontStyle.FONT_SLANT_UPRIGHT;
289 
290             File file = getTempFile();
291             try (InputStream is = am.open(path)) {
292                 assertTrue(copyToFile(file, is));
293 
294                 Font font = new Font.Builder(file).build();
295                 assertEquals(path, weight, font.getStyle().getWeight());
296                 assertEquals(path, slant, font.getStyle().getSlant());
297                 assertEquals(path, 0, font.getTtcIndex());
298                 assertNullOrEmpty(path, font.getAxes());
299                 assertNotNull(font.getBuffer());
300                 assertNotNull(font.getFile());
301             } finally {
302                 file.delete();
303             }
304         }
305     }
306 
307     @Test
testBuilder_file_ttc()308     public void testBuilder_file_ttc() throws IOException {
309         AssetManager am = InstrumentationRegistry.getTargetContext().getAssets();
310         for (Pair<Integer, Boolean> style : FontTestUtil.getAllStyles()) {
311             int weight = style.first.intValue();
312             boolean italic = style.second.booleanValue();
313             String path = FontTestUtil.getTtcFontFileInAsset();
314             int ttcIndex = FontTestUtil.getTtcIndexFromStyle(weight, italic);
315             final int slant = italic ? FontStyle.FONT_SLANT_ITALIC : FontStyle.FONT_SLANT_UPRIGHT;
316 
317             File file = getTempFile();
318             try (InputStream is = am.open(path)) {
319                 assertTrue(copyToFile(file, is));
320 
321                 Font font = new Font.Builder(file).setTtcIndex(ttcIndex).build();
322                 assertEquals(path, weight, font.getStyle().getWeight());
323                 assertEquals(path, slant, font.getStyle().getSlant());
324                 assertEquals(path, ttcIndex, font.getTtcIndex());
325                 assertNullOrEmpty(path, font.getAxes());
326                 assertNotNull(font.getBuffer());
327                 assertNotNull(font.getFile());
328             } finally {
329                 file.delete();
330             }
331         }
332     }
333 
334     @Test
testBuilder_file_vf()335     public void testBuilder_file_vf() throws IOException {
336         AssetManager am = InstrumentationRegistry.getTargetContext().getAssets();
337         for (Pair<Integer, Boolean> style : FontTestUtil.getAllStyles()) {
338             int weight = style.first.intValue();
339             boolean italic = style.second.booleanValue();
340             String path = FontTestUtil.getVFFontInAsset();
341             FontVariationAxis[] axes = FontVariationAxis.fromFontVariationSettings(
342                     FontTestUtil.getVarSettingsFromStyle(weight, italic));
343             final int slant = italic ? FontStyle.FONT_SLANT_ITALIC : FontStyle.FONT_SLANT_UPRIGHT;
344 
345             File file = getTempFile();
346             try (InputStream is = am.open(path)) {
347                 assertTrue(copyToFile(file, is));
348 
349                 Font font = new Font.Builder(file).setFontVariationSettings(axes).build();
350                 assertEquals(path, weight, font.getStyle().getWeight());
351                 assertEquals(path, slant, font.getStyle().getSlant());
352                 assertEquals(path, 0, font.getTtcIndex());
353                 assertAxesEquals(path, axes, font.getAxes());
354                 assertNotNull(font.getBuffer());
355                 assertNotNull(font.getFile());
356             } finally {
357                 file.delete();
358             }
359         }
360     }
361 
362     @Test
testBuilder_file_override()363     public void testBuilder_file_override() throws IOException {
364         int customWeight = 350;
365         AssetManager am = InstrumentationRegistry.getTargetContext().getAssets();
366         for (Pair<Integer, Boolean> style : FontTestUtil.getAllStyles()) {
367             int weight = style.first.intValue();
368             boolean italic = style.second.booleanValue();
369             String path = FontTestUtil.getFontPathFromStyle(weight, italic);
370             final int slant = italic ? FontStyle.FONT_SLANT_ITALIC : FontStyle.FONT_SLANT_UPRIGHT;
371 
372             File file = getTempFile();
373             try (InputStream is = am.open(path)) {
374                 assertTrue(copyToFile(file, is));
375 
376                 Font font = new Font.Builder(file).setWeight(customWeight).build();
377                 assertEquals(path, customWeight, font.getStyle().getWeight());
378                 assertEquals(path, slant, font.getStyle().getSlant());
379                 assertEquals(path, 0, font.getTtcIndex());
380                 assertNullOrEmpty(path, font.getAxes());
381                 assertNotNull(font.getBuffer());
382                 assertNotNull(font.getFile());
383             } finally {
384                 file.delete();
385             }
386         }
387         for (Pair<Integer, Boolean> style : FontTestUtil.getAllStyles()) {
388             int weight = style.first.intValue();
389             boolean italic = style.second.booleanValue();
390             String path = FontTestUtil.getFontPathFromStyle(weight, italic);
391 
392             File file = getTempFile();
393             try (InputStream is = am.open(path)) {
394                 assertTrue(copyToFile(file, is));
395 
396                 Font font = new Font.Builder(file).setSlant(FontStyle.FONT_SLANT_ITALIC).build();
397                 assertEquals(path, weight, font.getStyle().getWeight());
398                 assertEquals(path, FontStyle.FONT_SLANT_ITALIC, font.getStyle().getSlant());
399                 assertEquals(path, 0, font.getTtcIndex());
400                 assertNullOrEmpty(path, font.getAxes());
401                 assertNotNull(font.getBuffer());
402                 assertNotNull(font.getFile());
403             } finally {
404                 file.delete();
405             }
406         }
407     }
408 
409     @Test(expected = NullPointerException.class)
testBuilder_file_invalid_null_file()410     public void testBuilder_file_invalid_null_file() throws IOException {
411         File file = null;
412         new Font.Builder(file);
413     }
414 
415     @Test(expected = IOException.class)
testBuilder_file_invalid_not_found_file()416     public void testBuilder_file_invalid_not_found_file() throws IOException {
417         File file = new File("/no/such/file");
418         new Font.Builder(file).build();
419     }
420 
421     @Test
testBuilder_fd()422     public void testBuilder_fd() throws IOException {
423         AssetManager am = InstrumentationRegistry.getTargetContext().getAssets();
424         for (Pair<Integer, Boolean> style : FontTestUtil.getAllStyles()) {
425             int weight = style.first.intValue();
426             boolean italic = style.second.booleanValue();
427             String path = FontTestUtil.getFontPathFromStyle(weight, italic);
428             final int slant = italic ? FontStyle.FONT_SLANT_ITALIC : FontStyle.FONT_SLANT_UPRIGHT;
429 
430             File file = getTempFile();
431             try (InputStream is = am.open(path)) {
432                 assertTrue(copyToFile(file, is));
433 
434                 try (ParcelFileDescriptor fd = ParcelFileDescriptor.open(file,
435                         ParcelFileDescriptor.MODE_READ_ONLY)) {
436                     Font font = new Font.Builder(fd).build();
437                     assertEquals(path, weight, font.getStyle().getWeight());
438                     assertEquals(path, slant, font.getStyle().getSlant());
439                     assertEquals(path, 0, font.getTtcIndex());
440                     assertNullOrEmpty(path, font.getAxes());
441                     assertNotNull(font.getBuffer());
442                     assertNull(font.getFile());
443                 }
444             } finally {
445                 file.delete();
446             }
447         }
448     }
449 
450     @Test
testBuilder_fd_ttc()451     public void testBuilder_fd_ttc() throws IOException {
452         AssetManager am = InstrumentationRegistry.getTargetContext().getAssets();
453         for (Pair<Integer, Boolean> style : FontTestUtil.getAllStyles()) {
454             int weight = style.first.intValue();
455             boolean italic = style.second.booleanValue();
456             String path = FontTestUtil.getTtcFontFileInAsset();
457             int ttcIndex = FontTestUtil.getTtcIndexFromStyle(weight, italic);
458             final int slant = italic ? FontStyle.FONT_SLANT_ITALIC : FontStyle.FONT_SLANT_UPRIGHT;
459 
460             File file = getTempFile();
461             try (InputStream is = am.open(path)) {
462                 assertTrue(copyToFile(file, is));
463 
464                 try (ParcelFileDescriptor fd = ParcelFileDescriptor.open(file,
465                         ParcelFileDescriptor.MODE_READ_ONLY)) {
466                     Font font = new Font.Builder(fd).setTtcIndex(ttcIndex).build();
467                     assertEquals(path, weight, font.getStyle().getWeight());
468                     assertEquals(path, slant, font.getStyle().getSlant());
469                     assertEquals(path, ttcIndex, font.getTtcIndex());
470                     assertNullOrEmpty(path, font.getAxes());
471                     assertNotNull(font.getBuffer());
472                     assertNull(font.getFile());
473                 }
474             } finally {
475                 file.delete();
476             }
477         }
478     }
479 
480     @Test
testBuilder_fd_vf()481     public void testBuilder_fd_vf() throws IOException {
482         AssetManager am = InstrumentationRegistry.getTargetContext().getAssets();
483         for (Pair<Integer, Boolean> style : FontTestUtil.getAllStyles()) {
484             int weight = style.first.intValue();
485             boolean italic = style.second.booleanValue();
486             String path = FontTestUtil.getVFFontInAsset();
487             FontVariationAxis[] axes = FontVariationAxis.fromFontVariationSettings(
488                     FontTestUtil.getVarSettingsFromStyle(weight, italic));
489             final int slant = italic ? FontStyle.FONT_SLANT_ITALIC : FontStyle.FONT_SLANT_UPRIGHT;
490 
491             File file = getTempFile();
492             try (InputStream is = am.open(path)) {
493                 assertTrue(copyToFile(file, is));
494 
495                 try (ParcelFileDescriptor fd = ParcelFileDescriptor.open(file,
496                         ParcelFileDescriptor.MODE_READ_ONLY)) {
497                     Font font = new Font.Builder(fd).setFontVariationSettings(axes)
498                             .build();
499                     assertEquals(path, weight, font.getStyle().getWeight());
500                     assertEquals(path, slant, font.getStyle().getSlant());
501                     assertEquals(path, 0, font.getTtcIndex());
502                     assertAxesEquals(path, axes, font.getAxes());
503                     assertNotNull(font.getBuffer());
504                     assertNull(font.getFile());
505                 }
506             } finally {
507                 file.delete();
508             }
509         }
510     }
511 
512     @Test
testBuilder_fd_override()513     public void testBuilder_fd_override() throws IOException {
514         int customWeight = 350;
515         AssetManager am = InstrumentationRegistry.getTargetContext().getAssets();
516         for (Pair<Integer, Boolean> style : FontTestUtil.getAllStyles()) {
517             int weight = style.first.intValue();
518             boolean italic = style.second.booleanValue();
519             String path = FontTestUtil.getFontPathFromStyle(weight, italic);
520             final int slant = italic ? FontStyle.FONT_SLANT_ITALIC : FontStyle.FONT_SLANT_UPRIGHT;
521 
522             File file = getTempFile();
523             try (InputStream is = am.open(path)) {
524                 assertTrue(copyToFile(file, is));
525 
526                 try (ParcelFileDescriptor fd = ParcelFileDescriptor.open(file,
527                         ParcelFileDescriptor.MODE_READ_ONLY)) {
528                     Font font = new Font.Builder(fd).setWeight(customWeight).build();
529                     assertEquals(path, customWeight, font.getStyle().getWeight());
530                     assertEquals(path, slant, font.getStyle().getSlant());
531                     assertEquals(path, 0, font.getTtcIndex());
532                     assertNullOrEmpty(path, font.getAxes());
533                     assertNotNull(font.getBuffer());
534                     assertNull(font.getFile());
535                 }
536             } finally {
537                 file.delete();
538             }
539         }
540         for (Pair<Integer, Boolean> style : FontTestUtil.getAllStyles()) {
541             int weight = style.first.intValue();
542             boolean italic = style.second.booleanValue();
543             String path = FontTestUtil.getFontPathFromStyle(weight, italic);
544             final int slant = italic ? FontStyle.FONT_SLANT_ITALIC : FontStyle.FONT_SLANT_UPRIGHT;
545 
546             File file = getTempFile();
547             try (InputStream is = am.open(path)) {
548                 assertTrue(copyToFile(file, is));
549 
550                 try (ParcelFileDescriptor fd = ParcelFileDescriptor.open(file,
551                         ParcelFileDescriptor.MODE_READ_ONLY)) {
552                     Font font = new Font.Builder(fd).setSlant(
553                             FontStyle.FONT_SLANT_ITALIC).build();
554                     assertEquals(path, weight, font.getStyle().getWeight());
555                     assertEquals(path, FontStyle.FONT_SLANT_ITALIC, font.getStyle().getSlant());
556                     assertEquals(path, 0, font.getTtcIndex());
557                     assertNullOrEmpty(path, font.getAxes());
558                     assertNotNull(font.getBuffer());
559                     assertNull(font.getFile());
560                 }
561             } finally {
562                 file.delete();
563             }
564         }
565     }
566 
567     @Test
testBuilder_fd_subdata()568     public void testBuilder_fd_subdata() throws IOException {
569         byte[] placeHolderData = { (byte) 0xde, (byte) 0xad, (byte) 0xbe, (byte) 0xef };
570         AssetManager am = InstrumentationRegistry.getTargetContext().getAssets();
571         for (Pair<Integer, Boolean> style : FontTestUtil.getAllStyles()) {
572             int weight = style.first.intValue();
573             boolean italic = style.second.booleanValue();
574             String path = FontTestUtil.getFontPathFromStyle(weight, italic);
575             final int slant = italic ? FontStyle.FONT_SLANT_ITALIC : FontStyle.FONT_SLANT_UPRIGHT;
576 
577             File file = getTempFile();
578             try (InputStream is = am.open(path)) {
579                 assertTrue(copyToFile(file, is, placeHolderData, placeHolderData));
580 
581                 try (ParcelFileDescriptor fd = ParcelFileDescriptor.open(file,
582                         ParcelFileDescriptor.MODE_READ_ONLY)) {
583                     Font font = new Font.Builder(fd, placeHolderData.length,
584                             file.length() - placeHolderData.length * 2).build();
585                     assertEquals(path, weight, font.getStyle().getWeight());
586                     assertEquals(path, slant, font.getStyle().getSlant());
587                     assertEquals(path, 0, font.getTtcIndex());
588                     assertNullOrEmpty(path, font.getAxes());
589                     assertNotNull(font.getBuffer());
590                     assertNull(font.getFile());
591                 }
592             } finally {
593                 file.delete();
594             }
595         }
596     }
597 
598     @Test
testBuilder_fd_subdata_ttc()599     public void testBuilder_fd_subdata_ttc() throws IOException {
600         byte[] placeHolderData = { (byte) 0xde, (byte) 0xad, (byte) 0xbe, (byte) 0xef };
601         AssetManager am = InstrumentationRegistry.getTargetContext().getAssets();
602         for (Pair<Integer, Boolean> style : FontTestUtil.getAllStyles()) {
603             int weight = style.first.intValue();
604             boolean italic = style.second.booleanValue();
605             String path = FontTestUtil.getTtcFontFileInAsset();
606             int ttcIndex = FontTestUtil.getTtcIndexFromStyle(weight, italic);
607             final int slant = italic ? FontStyle.FONT_SLANT_ITALIC : FontStyle.FONT_SLANT_UPRIGHT;
608 
609             File file = getTempFile();
610             try (InputStream is = am.open(path)) {
611                 assertTrue(copyToFile(file, is, placeHolderData, placeHolderData));
612 
613                 try (ParcelFileDescriptor fd = ParcelFileDescriptor.open(file,
614                         ParcelFileDescriptor.MODE_READ_ONLY)) {
615                     Font font = new Font.Builder(
616                             fd, placeHolderData.length, file.length() - placeHolderData.length * 2)
617                             .setTtcIndex(ttcIndex).build();
618                     assertEquals(path, weight, font.getStyle().getWeight());
619                     assertEquals(path, slant, font.getStyle().getSlant());
620                     assertEquals(path, ttcIndex, font.getTtcIndex());
621                     assertNullOrEmpty(path, font.getAxes());
622                     assertNotNull(font.getBuffer());
623                     assertNull(font.getFile());
624                 }
625             } finally {
626                 file.delete();
627             }
628         }
629     }
630 
631     @Test
testBuilder_fd_subdata_vf()632     public void testBuilder_fd_subdata_vf() throws IOException {
633         byte[] placeHolderData = { (byte) 0xde, (byte) 0xad, (byte) 0xbe, (byte) 0xef };
634         AssetManager am = InstrumentationRegistry.getTargetContext().getAssets();
635         for (Pair<Integer, Boolean> style : FontTestUtil.getAllStyles()) {
636             int weight = style.first.intValue();
637             boolean italic = style.second.booleanValue();
638             String path = FontTestUtil.getVFFontInAsset();
639             FontVariationAxis[] axes = FontVariationAxis.fromFontVariationSettings(
640                     FontTestUtil.getVarSettingsFromStyle(weight, italic));
641             final int slant = italic ? FontStyle.FONT_SLANT_ITALIC : FontStyle.FONT_SLANT_UPRIGHT;
642 
643             File file = getTempFile();
644             try (InputStream is = am.open(path)) {
645                 assertTrue(copyToFile(file, is, placeHolderData, placeHolderData));
646 
647                 try (ParcelFileDescriptor fd = ParcelFileDescriptor.open(file,
648                         ParcelFileDescriptor.MODE_READ_ONLY)) {
649                     Font font = new Font.Builder(
650                             fd, placeHolderData.length, file.length() - placeHolderData.length * 2)
651                             .setFontVariationSettings(axes).build();
652                     assertEquals(path, weight, font.getStyle().getWeight());
653                     assertEquals(path, slant, font.getStyle().getSlant());
654                     assertEquals(path, 0, font.getTtcIndex());
655                     assertAxesEquals(path, axes, font.getAxes());
656                     assertNotNull(font.getBuffer());
657                     assertNull(font.getFile());
658                 }
659             } finally {
660                 file.delete();
661             }
662         }
663     }
664 
665     @Test
testBuilder_fd_subdata_override()666     public void testBuilder_fd_subdata_override() throws IOException {
667         int customWeight = 350;
668         byte[] placeHolderData = { (byte) 0xde, (byte) 0xad, (byte) 0xbe, (byte) 0xef };
669         AssetManager am = InstrumentationRegistry.getTargetContext().getAssets();
670         for (Pair<Integer, Boolean> style : FontTestUtil.getAllStyles()) {
671             int weight = style.first.intValue();
672             boolean italic = style.second.booleanValue();
673             String path = FontTestUtil.getFontPathFromStyle(weight, italic);
674             final int slant = italic ? FontStyle.FONT_SLANT_ITALIC : FontStyle.FONT_SLANT_UPRIGHT;
675 
676             File file = getTempFile();
677             try (InputStream is = am.open(path)) {
678                 assertTrue(copyToFile(file, is, placeHolderData, placeHolderData));
679 
680                 try (ParcelFileDescriptor fd = ParcelFileDescriptor.open(file,
681                         ParcelFileDescriptor.MODE_READ_ONLY)) {
682                     Font font = new Font.Builder(
683                             fd, placeHolderData.length, file.length() - placeHolderData.length * 2)
684                             .setWeight(customWeight).build();
685                     assertEquals(path, customWeight, font.getStyle().getWeight());
686                     assertEquals(path, slant, font.getStyle().getSlant());
687                     assertEquals(path, 0, font.getTtcIndex());
688                     assertNullOrEmpty(path, font.getAxes());
689                     assertNotNull(font.getBuffer());
690                     assertNull(font.getFile());
691                 }
692             } finally {
693                 file.delete();
694             }
695         }
696 
697         for (Pair<Integer, Boolean> style : FontTestUtil.getAllStyles()) {
698             int weight = style.first.intValue();
699             boolean italic = style.second.booleanValue();
700             String path = FontTestUtil.getFontPathFromStyle(weight, italic);
701 
702             File file = getTempFile();
703             try (InputStream is = am.open(path)) {
704                 assertTrue(copyToFile(file, is, placeHolderData, placeHolderData));
705 
706                 try (ParcelFileDescriptor fd = ParcelFileDescriptor.open(file,
707                         ParcelFileDescriptor.MODE_READ_ONLY)) {
708                     Font font = new Font.Builder(
709                             fd, placeHolderData.length, file.length() - placeHolderData.length * 2)
710                             .setSlant(FontStyle.FONT_SLANT_ITALIC).build();
711                     assertEquals(path, weight, font.getStyle().getWeight());
712                     assertEquals(path, FontStyle.FONT_SLANT_ITALIC, font.getStyle().getSlant());
713                     assertEquals(path, 0, font.getTtcIndex());
714                     assertNullOrEmpty(path, font.getAxes());
715                     assertNotNull(font.getBuffer());
716                     assertNull(font.getFile());
717                 }
718             } finally {
719                 file.delete();
720             }
721         }
722     }
723 
724     @Test(expected = NullPointerException.class)
testBuilder_fd_invalid_null()725     public void testBuilder_fd_invalid_null() throws IOException {
726         ParcelFileDescriptor fd = null;
727         new Font.Builder(fd);
728     }
729 
730     @Test(expected = NullPointerException.class)
testBuilder_fd_subadata_invalid_null()731     public void testBuilder_fd_subadata_invalid_null() throws IOException {
732         ParcelFileDescriptor fd = null;
733         new Font.Builder(fd, 0, -1);
734     }
735 
736     @Test(expected = IOException.class)
testBuilder_fd_subadata_invalid_invalid_size()737     public void testBuilder_fd_subadata_invalid_invalid_size() throws IOException {
738         AssetManager am = InstrumentationRegistry.getTargetContext().getAssets();
739         String path = FontTestUtil.getFontPathFromStyle(400, false);
740 
741         File file = getTempFile();
742         try (InputStream is = am.open(path)) {
743             assertTrue(copyToFile(file, is));
744             try (ParcelFileDescriptor fd = ParcelFileDescriptor.open(file,
745                     ParcelFileDescriptor.MODE_READ_ONLY)) {
746                 new Font.Builder(fd, 0, Integer.MAX_VALUE).build();
747             }
748         }
749     }
750 
751     @Test(expected = IOException.class)
testBuilder_fd_subadata_invalid_invalid_offset()752     public void testBuilder_fd_subadata_invalid_invalid_offset() throws IOException {
753         AssetManager am = InstrumentationRegistry.getTargetContext().getAssets();
754         String path = FontTestUtil.getFontPathFromStyle(400, false);
755 
756         File file = getTempFile();
757         try (InputStream is = am.open(path)) {
758             assertTrue(copyToFile(file, is));
759             try (ParcelFileDescriptor fd = ParcelFileDescriptor.open(file,
760                     ParcelFileDescriptor.MODE_READ_ONLY)) {
761                 new Font.Builder(fd, Integer.MAX_VALUE, file.length()).build();
762             }
763         }
764     }
765 
766     @Test
testBuilder_asset()767     public void testBuilder_asset() throws IOException {
768         AssetManager am = InstrumentationRegistry.getTargetContext().getAssets();
769         for (Pair<Integer, Boolean> style : FontTestUtil.getAllStyles()) {
770             int weight = style.first.intValue();
771             boolean italic = style.second.booleanValue();
772             String path = FontTestUtil.getFontPathFromStyle(weight, italic);
773             final int slant = italic ? FontStyle.FONT_SLANT_ITALIC : FontStyle.FONT_SLANT_UPRIGHT;
774 
775             Font font = new Font.Builder(am, path).build();
776             assertEquals(path, weight, font.getStyle().getWeight());
777             assertEquals(path, slant, font.getStyle().getSlant());
778             assertEquals(path, 0, font.getTtcIndex());
779             assertNullOrEmpty(path, font.getAxes());
780             assertNotNull(font.getBuffer());
781             assertNull(font.getFile());
782         }
783     }
784 
785     @Test
testBuilder_asset_ttc()786     public void testBuilder_asset_ttc() throws IOException {
787         AssetManager am = InstrumentationRegistry.getTargetContext().getAssets();
788         for (Pair<Integer, Boolean> style : FontTestUtil.getAllStyles()) {
789             int weight = style.first.intValue();
790             boolean italic = style.second.booleanValue();
791             String path = FontTestUtil.getTtcFontFileInAsset();
792             int ttcIndex = FontTestUtil.getTtcIndexFromStyle(weight, italic);
793             final int slant = italic ? FontStyle.FONT_SLANT_ITALIC : FontStyle.FONT_SLANT_UPRIGHT;
794 
795             Font font = new Font.Builder(am, path).setTtcIndex(ttcIndex).build();
796             assertEquals(path, weight, font.getStyle().getWeight());
797             assertEquals(path, slant, font.getStyle().getSlant());
798             assertEquals(path, ttcIndex, font.getTtcIndex());
799             assertNullOrEmpty(path, font.getAxes());
800             assertNotNull(font.getBuffer());
801             assertNull(font.getFile());
802         }
803     }
804 
805     @Test
testBuilder_asset_vf()806     public void testBuilder_asset_vf() throws IOException {
807         AssetManager am = InstrumentationRegistry.getTargetContext().getAssets();
808         for (Pair<Integer, Boolean> style : FontTestUtil.getAllStyles()) {
809             int weight = style.first.intValue();
810             boolean italic = style.second.booleanValue();
811             String path = FontTestUtil.getVFFontInAsset();
812             FontVariationAxis[] axes = FontVariationAxis.fromFontVariationSettings(
813                     FontTestUtil.getVarSettingsFromStyle(weight, italic));
814             final int slant = italic ? FontStyle.FONT_SLANT_ITALIC : FontStyle.FONT_SLANT_UPRIGHT;
815 
816             Font font = new Font.Builder(am, path).setFontVariationSettings(axes).build();
817             assertEquals(path, weight, font.getStyle().getWeight());
818             assertEquals(path, slant, font.getStyle().getSlant());
819             assertEquals(path, 0, font.getTtcIndex());
820             assertAxesEquals(path, axes, font.getAxes());
821             assertNotNull(font.getBuffer());
822             assertNull(font.getFile());
823         }
824     }
825 
826     @Test
testBuilder_asset_override()827     public void testBuilder_asset_override() throws IOException {
828         int customWeight = 350;
829         AssetManager am = InstrumentationRegistry.getTargetContext().getAssets();
830         for (Pair<Integer, Boolean> style : FontTestUtil.getAllStyles()) {
831             int weight = style.first.intValue();
832             boolean italic = style.second.booleanValue();
833             String path = FontTestUtil.getFontPathFromStyle(weight, italic);
834             final int slant = italic ? FontStyle.FONT_SLANT_ITALIC : FontStyle.FONT_SLANT_UPRIGHT;
835 
836             Font font = new Font.Builder(am, path).setWeight(customWeight).build();
837             assertEquals(path, customWeight, font.getStyle().getWeight());
838             assertEquals(path, slant, font.getStyle().getSlant());
839             assertEquals(path, 0, font.getTtcIndex());
840             assertNullOrEmpty(path, font.getAxes());
841             assertNotNull(font.getBuffer());
842             assertNull(font.getFile());
843         }
844         for (Pair<Integer, Boolean> style : FontTestUtil.getAllStyles()) {
845             int weight = style.first.intValue();
846             boolean italic = style.second.booleanValue();
847             String path = FontTestUtil.getFontPathFromStyle(weight, italic);
848 
849             Font font = new Font.Builder(am, path).setSlant(FontStyle.FONT_SLANT_ITALIC).build();
850             assertEquals(path, weight, font.getStyle().getWeight());
851             assertEquals(path, FontStyle.FONT_SLANT_ITALIC, font.getStyle().getSlant());
852             assertEquals(path, 0, font.getTtcIndex());
853             assertNullOrEmpty(path, font.getAxes());
854             assertNotNull(font.getBuffer());
855             assertNull(font.getFile());
856         }
857     }
858 
859     @Test(expected = NullPointerException.class)
testBuilder_asset_invalid_null_asset()860     public void testBuilder_asset_invalid_null_asset() throws IOException {
861         AssetManager am = null;
862         new Font.Builder(am, "/some/path");
863     }
864 
865     @Test(expected = IOException.class)
testBuilder_asset_invalid_not_found()866     public void testBuilder_asset_invalid_not_found() throws IOException {
867         AssetManager am = InstrumentationRegistry.getTargetContext().getAssets();
868         new Font.Builder(am, "/no/such/file").build();
869     }
870 
871     @Test
testBuilder_resource()872     public void testBuilder_resource() throws IOException {
873         Resources res = InstrumentationRegistry.getTargetContext().getResources();
874         for (Pair<Integer, Boolean> style : FontTestUtil.getAllStyles()) {
875             int weight = style.first.intValue();
876             boolean italic = style.second.booleanValue();
877             int resId = FontTestUtil.getFontResourceIdFromStyle(weight, italic);
878             final int slant = italic ? FontStyle.FONT_SLANT_ITALIC : FontStyle.FONT_SLANT_UPRIGHT;
879 
880             Font font = new Font.Builder(res, resId).build();
881             assertEquals("ResId=#" + resId, weight, font.getStyle().getWeight());
882             assertEquals("ResId=#" + resId, slant, font.getStyle().getSlant());
883             assertEquals("ResId=#" + resId, 0, font.getTtcIndex());
884             assertNullOrEmpty("ResId=#" + resId, font.getAxes());
885             assertNotNull("ResId=#" + resId, font.getBuffer());
886             assertNull("ResId=#" + resId, font.getFile());
887         }
888     }
889 
890     @Test
testBuilder_resource_ttc()891     public void testBuilder_resource_ttc() throws IOException {
892         Resources res = InstrumentationRegistry.getTargetContext().getResources();
893         for (Pair<Integer, Boolean> style : FontTestUtil.getAllStyles()) {
894             int weight = style.first.intValue();
895             boolean italic = style.second.booleanValue();
896             int resId = FontTestUtil.getTtcFontFileResourceId();
897             int ttcIndex = FontTestUtil.getTtcIndexFromStyle(weight, italic);
898             final int slant = italic ? FontStyle.FONT_SLANT_ITALIC : FontStyle.FONT_SLANT_UPRIGHT;
899 
900             Font font = new Font.Builder(res, resId).setTtcIndex(ttcIndex).build();
901             assertEquals("ResId=#" + resId, weight, font.getStyle().getWeight());
902             assertEquals("ResId=#" + resId, slant, font.getStyle().getSlant());
903             assertEquals("ResId=#" + resId, ttcIndex, font.getTtcIndex());
904             assertNullOrEmpty("ResId=#" + resId, font.getAxes());
905             assertNotNull("ResId=#" + resId, font.getBuffer());
906             assertNull("ResId=#" + resId, font.getFile());
907         }
908     }
909 
910     @Test
testBuilder_resource_vf()911     public void testBuilder_resource_vf() throws IOException {
912         Resources res = InstrumentationRegistry.getTargetContext().getResources();
913         for (Pair<Integer, Boolean> style : FontTestUtil.getAllStyles()) {
914             int weight = style.first.intValue();
915             boolean italic = style.second.booleanValue();
916             int resId = FontTestUtil.getVFFontResourceId();
917             FontVariationAxis[] axes = FontVariationAxis.fromFontVariationSettings(
918                     FontTestUtil.getVarSettingsFromStyle(weight, italic));
919             final int slant = italic ? FontStyle.FONT_SLANT_ITALIC : FontStyle.FONT_SLANT_UPRIGHT;
920 
921             Font font = new Font.Builder(res, resId).setFontVariationSettings(axes).build();
922             assertEquals("ResId=#" + resId, weight, font.getStyle().getWeight());
923             assertEquals("ResId=#" + resId, slant, font.getStyle().getSlant());
924             assertEquals("ResId=#" + resId, 0, font.getTtcIndex());
925             assertAxesEquals("ResId=#" + resId, axes, font.getAxes());
926             assertNotNull("ResId=#" + font.getBuffer());
927             assertNull("ResId=#" + resId, font.getFile());
928         }
929     }
930 
931     @Test
testBuilder_resource_override()932     public void testBuilder_resource_override() throws IOException {
933         int customWeight = 350;
934         Resources res = InstrumentationRegistry.getTargetContext().getResources();
935         for (Pair<Integer, Boolean> style : FontTestUtil.getAllStyles()) {
936             int weight = style.first.intValue();
937             boolean italic = style.second.booleanValue();
938             int resId = FontTestUtil.getFontResourceIdFromStyle(weight, italic);
939             final int slant = italic ? FontStyle.FONT_SLANT_ITALIC : FontStyle.FONT_SLANT_UPRIGHT;
940 
941             Font font = new Font.Builder(res, resId).setWeight(customWeight).build();
942             assertEquals("ResId=#" + resId, customWeight, font.getStyle().getWeight());
943             assertEquals("ResId=#" + resId, slant, font.getStyle().getSlant());
944             assertEquals("ResId=#" + resId, 0, font.getTtcIndex());
945             assertNullOrEmpty("ResId=#" + resId, font.getAxes());
946             assertNotNull("ResId=#" + resId, font.getBuffer());
947             assertNull("ResId=#" + resId, font.getFile());
948         }
949 
950         for (Pair<Integer, Boolean> style : FontTestUtil.getAllStyles()) {
951             int weight = style.first.intValue();
952             boolean italic = style.second.booleanValue();
953             int resId = FontTestUtil.getFontResourceIdFromStyle(weight, italic);
954 
955             Font font = new Font.Builder(res, resId).setSlant(FontStyle.FONT_SLANT_ITALIC).build();
956             assertEquals("ResId=#" + resId, weight, font.getStyle().getWeight());
957             assertEquals("ResId=#" + resId, FontStyle.FONT_SLANT_ITALIC,
958                     font.getStyle().getSlant());
959             assertEquals("ResId=#" + resId, 0, font.getTtcIndex());
960             assertNullOrEmpty("ResId=#" + resId, font.getAxes());
961             assertNotNull("ResId=#" + resId, font.getBuffer());
962             assertNull("ResId=#" + resId, font.getFile());
963         }
964     }
965 
966     @Test(expected = NullPointerException.class)
testBuilder_resource_invalid_null_resource()967     public void testBuilder_resource_invalid_null_resource() throws IOException {
968         Resources res = null;
969         new Font.Builder(res, R.font.ascii);
970     }
971 
972     @Test(expected = NotFoundException.class)
testBuilder_resource_invalid_res_id()973     public void testBuilder_resource_invalid_res_id() throws IOException {
974         Resources res = InstrumentationRegistry.getTargetContext().getResources();
975         new Font.Builder(res, -1).build();
976     }
977 
978     @Test(expected = IOException.class)
testBuilder_asset_invalid_xml_font()979     public void testBuilder_asset_invalid_xml_font() throws IOException {
980         Resources res = InstrumentationRegistry.getTargetContext().getResources();
981         new Font.Builder(res, R.font.multiweight_family /* XML font */).build();
982     }
983 
984     @Test
testEquals()985     public void testEquals() throws IOException {
986         Resources res = InstrumentationRegistry.getTargetContext().getResources();
987         ArrayList<Font> fonts1 = new ArrayList<>();
988         ArrayList<Font> fonts2 = new ArrayList<>();
989         for (Pair<Integer, Boolean> style : FontTestUtil.getAllStyles()) {
990             int weight = style.first.intValue();
991             boolean italic = style.second.booleanValue();
992             int resId = FontTestUtil.getFontResourceIdFromStyle(weight, italic);
993 
994             fonts1.add(new Font.Builder(res, resId).build());
995             fonts2.add(new Font.Builder(res, resId).build());
996         }
997 
998         for (int i = 0; i < fonts1.size(); ++i) {
999             assertTrue(fonts1.get(i).equals(fonts1.get(i)));
1000             assertTrue(fonts1.get(i).equals(fonts2.get(i)));
1001             assertTrue(fonts2.get(i).equals(fonts1.get(i)));
1002         }
1003 
1004         for (int i = 0; i < fonts1.size(); ++i) {
1005             for (int j = i + 1; j < fonts1.size(); ++j) {
1006                 assertFalse(fonts1.get(i).equals(fonts1.get(j)));
1007                 assertFalse(fonts1.get(j).equals(fonts1.get(i)));
1008             }
1009         }
1010     }
1011 
1012     @Test
testHashCode()1013     public void testHashCode() throws IOException {
1014         Resources res = InstrumentationRegistry.getTargetContext().getResources();
1015         ArrayList<Font> fonts1 = new ArrayList<>();
1016         ArrayList<Font> fonts2 = new ArrayList<>();
1017         for (Pair<Integer, Boolean> style : FontTestUtil.getAllStyles()) {
1018             int weight = style.first.intValue();
1019             boolean italic = style.second.booleanValue();
1020             int resId = FontTestUtil.getFontResourceIdFromStyle(weight, italic);
1021 
1022             fonts1.add(new Font.Builder(res, resId).build());
1023             fonts2.add(new Font.Builder(res, resId).build());
1024         }
1025 
1026         for (int i = 0; i < fonts1.size(); ++i) {
1027             assertEquals(fonts1.get(i).hashCode(), fonts1.get(i).hashCode());
1028             assertEquals(fonts1.get(i).hashCode(), fonts2.get(i).hashCode());
1029         }
1030     }
1031 
1032     @Test(expected = IllegalArgumentException.class)
testMaxFontWeight()1033     public void testMaxFontWeight() throws IOException {
1034         final Resources res = InstrumentationRegistry.getTargetContext().getResources();
1035         new Font.Builder(res, R.font.ascii).setWeight(FontStyle.FONT_WEIGHT_MAX + 1).build();
1036     }
1037 
1038     @Test(expected = IllegalArgumentException.class)
testMinFontWeight()1039     public void testMinFontWeight() throws IOException {
1040         final Resources res = InstrumentationRegistry.getTargetContext().getResources();
1041         new Font.Builder(res, R.font.ascii).setWeight(FontStyle.FONT_WEIGHT_MIN - 1).build();
1042     }
1043 
1044     @Test
builder_with_font_with_axis()1045     public void builder_with_font_with_axis() throws IOException {
1046         AssetManager assets = InstrumentationRegistry.getTargetContext().getAssets();
1047 
1048         // WeightEqualsEmVariableFont adjust glyph advance as follows
1049         //  glyph advance = 'wght' value / 1000
1050         // Thus, by setting text size to 1000px, the glyph advance will equals to passed wght value.
1051         Font baseFont = new Font.Builder(assets, "fonts/var_fonts/WidthEqualsEmVariableFont.ttf")
1052                 .build();
1053 
1054         FontStyle style = new FontStyle(123, FontStyle.FONT_SLANT_ITALIC);
1055 
1056         for (int width = 50; width < 1000; width += 50) {
1057             Font clonedFont = new Font.Builder(baseFont)
1058                     .setWeight(style.getWeight())
1059                     .setSlant(style.getSlant())
1060                     .setFontVariationSettings("'wdth' " + width)
1061                     .build();
1062 
1063             // New font should have the same style passed.
1064             assertEquals(style.getWeight(), clonedFont.getStyle().getWeight());
1065             assertEquals(style.getSlant(), clonedFont.getStyle().getSlant());
1066 
1067             Paint p = new Paint();
1068             p.setTextSize(1000);  // make 1em = 1000px = weight
1069             p.setTypeface(new Typeface.CustomFallbackBuilder(
1070                     new FontFamily.Builder(clonedFont).build()
1071             ).build());
1072             assertEquals(width, p.measureText("a"), 0);
1073         }
1074     }
1075 
1076     @Test
builder_with_explicit_style()1077     public void builder_with_explicit_style() throws IOException {
1078         AssetManager assets = InstrumentationRegistry.getTargetContext().getAssets();
1079 
1080         Font baseFont = new Font.Builder(assets, "fonts/others/samplefont.ttf").build();
1081         FontStyle style = new FontStyle(123, FontStyle.FONT_SLANT_ITALIC);
1082         Font clonedFont = new Font.Builder(baseFont)
1083                 .setWeight(style.getWeight())
1084                 .setSlant(style.getSlant())
1085                 .build();
1086 
1087         assertEquals(style.getWeight(), clonedFont.getStyle().getWeight());
1088         assertEquals(style.getSlant(), clonedFont.getStyle().getSlant());
1089     }
1090 
1091     @Test
builder_style_resolve_default()1092     public void builder_style_resolve_default() throws IOException {
1093         AssetManager assets = InstrumentationRegistry.getTargetContext().getAssets();
1094 
1095         Font baseFont = new Font.Builder(assets,
1096                 "fonts/family_selection/ttf/ascii_l3em_weight600_italic.ttf").build();
1097         Font clonedFont = new Font.Builder(baseFont).build();
1098 
1099         assertEquals(600, clonedFont.getStyle().getWeight());
1100         assertEquals(FontStyle.FONT_SLANT_ITALIC, clonedFont.getStyle().getSlant());
1101     }
1102 
1103     @Test
getBoundingBox()1104     public void getBoundingBox() throws IOException {
1105         AssetManager assets = InstrumentationRegistry.getTargetContext().getAssets();
1106 
1107         Font font = new Font.Builder(assets, "fonts/measurement/a3em.ttf").build();
1108         Paint paint = new Paint();
1109         paint.setTextSize(100);  // make 1em = 100px
1110 
1111         int glyphID = 1;  // See a3em.ttx file for the Glyph ID.
1112 
1113         RectF rect = new RectF();
1114         float advance = font.getGlyphBounds(glyphID, paint, rect);
1115 
1116         assertEquals(100f, advance, 0f);
1117         // Glyph bbox is 0.1em shifted to right. See lsb value in hmtx in ttx file.
1118         assertEquals(rect.left, 10f, 0f);
1119         assertEquals(rect.top, -100f, 0f);
1120         assertEquals(rect.right, 110f, 0f);
1121         assertEquals(rect.bottom, 0f, 0f);
1122     }
1123 
1124     @Test
getFontMetrics()1125     public void getFontMetrics() throws IOException {
1126         AssetManager assets = InstrumentationRegistry.getTargetContext().getAssets();
1127 
1128         Font font = new Font.Builder(assets, "fonts/measurement/a3em.ttf").build();
1129         Paint paint = new Paint();
1130         paint.setTextSize(100);  // make 1em = 100px
1131 
1132         Paint.FontMetrics metrics = new Paint.FontMetrics();
1133         font.getMetrics(paint, metrics);
1134 
1135         assertEquals(-100f, metrics.ascent, 0f);
1136         assertEquals(20f, metrics.descent, 0f);
1137         // This refers head.yMax which is not explicitly visible in ttx file.
1138         assertEquals(-300f, metrics.top, 0f);
1139         // This refers head.yMin which is not explicitly visible in ttx file.
1140         assertEquals(0f, metrics.bottom, 0f);
1141     }
1142 
1143     @Test
byteBufferEquality()1144     public void byteBufferEquality() throws IOException {
1145         AssetManager assets = InstrumentationRegistry.getTargetContext().getAssets();
1146 
1147         Font aFont = new Font.Builder(assets, "fonts/others/samplefont.ttf").build();
1148         // Copied font must be equals to original one.
1149         Font bFont = new Font.Builder(aFont).build();
1150         assertEquals(aFont, bFont);
1151         assertEquals(bFont, aFont);
1152 
1153         // Same source font must be equal.
1154         Font cFont = new Font.Builder(assets, "fonts/others/samplefont.ttf").build();
1155         assertEquals(aFont, cFont);
1156         assertEquals(cFont, aFont);
1157 
1158         // Created font from duplicated buffers must be equal.
1159         Font dFont = new Font.Builder(aFont.getBuffer().duplicate()).build();
1160         Font eFont = new Font.Builder(aFont.getBuffer().duplicate()).build();
1161         assertEquals(dFont, eFont);
1162         assertEquals(eFont, dFont);
1163 
1164         // Different parameter should be unequal but sameSource returns true.
1165         Font fFont = new Font.Builder(aFont.getBuffer().duplicate())
1166                 .setFontVariationSettings("'wght' 400").build();
1167         assertNotEquals(aFont, fFont);
1168         assertNotEquals(fFont, aFont);
1169 
1170         // Different source must be not equals.
1171         Font gFont = new Font.Builder(assets, "fonts/others/samplefont2.ttf").build();
1172         assertNotEquals(aFont, gFont);
1173     }
1174 
1175     @Test
fontIdentifier()1176     public void fontIdentifier() throws IOException {
1177         AssetManager assets = InstrumentationRegistry.getTargetContext().getAssets();
1178 
1179         Font aFont = new Font.Builder(assets, "fonts/others/samplefont.ttf").build();
1180         // Copied font must be equals to original one.
1181         Font bFont = new Font.Builder(aFont).build();
1182         assertEquals(aFont.getSourceIdentifier(), bFont.getSourceIdentifier());
1183 
1184         // Different parameter should be unequal but sameSource returns true.
1185         Font dFont = new Font.Builder(aFont)
1186                 .setFontVariationSettings("'wght' 400")
1187                 .setWeight(123)
1188                 .build();
1189         assertEquals(aFont.getSourceIdentifier(), dFont.getSourceIdentifier());
1190 
1191         // Different source must be not equals.
1192         Font gFont = new Font.Builder(assets, "fonts/others/samplefont2.ttf").build();
1193         assertNotEquals(aFont.getSourceIdentifier(), gFont.getSourceIdentifier());
1194 
1195         Typeface typeface = new Typeface.CustomFallbackBuilder(
1196                 new FontFamily.Builder(
1197                         aFont
1198                 ).build()
1199         ).build();
1200 
1201         Paint paint = new Paint();
1202         paint.setTypeface(typeface);
1203         PositionedGlyphs glyphs = TextRunShaper.shapeTextRun("a", 0, 1, 0, 1, 0f, 0f, false, paint);
1204         assertEquals(aFont, glyphs.getFont(0));
1205         assertEquals(aFont.getSourceIdentifier(), glyphs.getFont(0).getSourceIdentifier());
1206     }
1207 
1208     @Test
byteBufferSameHash()1209     public void byteBufferSameHash() throws IOException {
1210         AssetManager assets = InstrumentationRegistry.getTargetContext().getAssets();
1211 
1212         Font aFont = new Font.Builder(assets, "fonts/others/samplefont.ttf").build();
1213         // Copied font must be equals to original one.
1214         assertEquals(new Font.Builder(aFont).build().hashCode(), aFont.hashCode());
1215 
1216         // Same source font must be equal.
1217         assertEquals(new Font.Builder(assets, "fonts/others/samplefont.ttf").build().hashCode(),
1218                 aFont.hashCode());
1219 
1220         // Created font from duplicated buffers must be equal.
1221         int cFontHash = new Font.Builder(aFont.getBuffer().duplicate()).build().hashCode();
1222         int dFontHash = new Font.Builder(aFont.getBuffer().duplicate()).build().hashCode();
1223         assertEquals(cFontHash, dFontHash);
1224     }
1225 }
1226