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; 18 19 import static org.junit.Assert.assertEquals; 20 import static org.junit.Assert.assertNotNull; 21 import static org.junit.Assert.assertTrue; 22 23 import android.content.Context; 24 import android.content.res.AssetManager; 25 import android.content.res.Resources; 26 import android.graphics.fonts.FontFamily; 27 import android.graphics.fonts.SystemFonts; 28 import android.os.SharedMemory; 29 import android.text.FontConfig; 30 import android.util.ArrayMap; 31 32 import androidx.test.InstrumentationRegistry; 33 import androidx.test.filters.LargeTest; 34 import androidx.test.filters.MediumTest; 35 import androidx.test.filters.SmallTest; 36 import androidx.test.runner.AndroidJUnit4; 37 38 import com.android.frameworks.coretests.R; 39 40 import org.junit.Test; 41 import org.junit.runner.RunWith; 42 43 import java.nio.ByteOrder; 44 import java.util.HashMap; 45 import java.util.Map; 46 import java.util.Random; 47 48 @RunWith(AndroidJUnit4.class) 49 public class TypefaceTest { 50 51 // create array of all std faces 52 private final Typeface[] mFaces = new Typeface[] { 53 Typeface.create(Typeface.SANS_SERIF, 0), 54 Typeface.create(Typeface.SANS_SERIF, 1), 55 Typeface.create(Typeface.SERIF, 0), 56 Typeface.create(Typeface.SERIF, 1), 57 Typeface.create(Typeface.SERIF, 2), 58 Typeface.create(Typeface.SERIF, 3), 59 Typeface.create(Typeface.MONOSPACE, 0) 60 }; 61 62 private static final int[] STYLES = { 63 Typeface.NORMAL, Typeface.BOLD, Typeface.ITALIC, Typeface.BOLD_ITALIC, 64 }; 65 66 @SmallTest 67 @Test testBasic()68 public void testBasic() throws Exception { 69 assertTrue("basic", Typeface.DEFAULT != null); 70 assertTrue("basic", Typeface.DEFAULT_BOLD != null); 71 assertTrue("basic", Typeface.SANS_SERIF != null); 72 assertTrue("basic", Typeface.SERIF != null); 73 assertTrue("basic", Typeface.MONOSPACE != null); 74 } 75 76 @SmallTest 77 @Test testDefaults()78 public void testDefaults() { 79 for (int style : STYLES) { 80 String msg = "style = " + style; 81 assertNotNull(msg, Typeface.defaultFromStyle(style)); 82 assertEquals(msg, style, Typeface.defaultFromStyle(style).getStyle()); 83 } 84 } 85 86 @SmallTest 87 @Test testUnique()88 public void testUnique() throws Exception { 89 final int n = mFaces.length; 90 for (int i = 0; i < n; i++) { 91 for (int j = i + 1; j < n; j++) { 92 assertTrue("unique", mFaces[i] != mFaces[j]); 93 } 94 } 95 } 96 97 @SmallTest 98 @Test testStyles()99 public void testStyles() throws Exception { 100 assertTrue("style", mFaces[0].getStyle() == Typeface.NORMAL); 101 assertTrue("style", mFaces[1].getStyle() == Typeface.BOLD); 102 assertTrue("style", mFaces[2].getStyle() == Typeface.NORMAL); 103 assertTrue("style", mFaces[3].getStyle() == Typeface.BOLD); 104 assertTrue("style", mFaces[4].getStyle() == Typeface.ITALIC); 105 assertTrue("style", mFaces[5].getStyle() == Typeface.BOLD_ITALIC); 106 assertTrue("style", mFaces[6].getStyle() == Typeface.NORMAL); 107 } 108 109 @MediumTest 110 @Test testUniformY()111 public void testUniformY() throws Exception { 112 Paint p = new Paint(); 113 final int n = mFaces.length; 114 for (int i = 1; i <= 36; i++) { 115 p.setTextSize(i); 116 float ascent = 0; 117 float descent = 0; 118 for (int j = 0; j < n; j++) { 119 p.setTypeface(mFaces[j]); 120 Paint.FontMetrics fm = p.getFontMetrics(); 121 if (j == 0) { 122 ascent = fm.ascent; 123 descent = fm.descent; 124 } else { 125 assertTrue("fontMetrics", fm.ascent == ascent); 126 assertTrue("fontMetrics", fm.descent == descent); 127 } 128 } 129 } 130 } 131 132 @LargeTest 133 @Test testMultithreadCacheStressTest()134 public void testMultithreadCacheStressTest() { 135 final Context context = InstrumentationRegistry.getInstrumentation().getTargetContext(); 136 final Resources res = context.getResources(); 137 final AssetManager assets = res.getAssets(); 138 final Typeface[] baseTypefaces = { 139 null, 140 Typeface.SANS_SERIF, 141 Typeface.SERIF, 142 Typeface.MONOSPACE, 143 res.getFont(R.font.samplefont), 144 res.getFont(R.font.samplefont2), 145 res.getFont(R.font.samplefont3), 146 res.getFont(R.font.samplefont4), 147 res.getFont(R.font.samplexmlfont), 148 Typeface.createFromAsset(assets, "fonts/a3em.ttf"), 149 Typeface.createFromAsset(assets, "fonts/b3em.ttf"), 150 Typeface.createFromAsset(assets, "fonts/c3em.ttf"), 151 Typeface.createFromAsset(assets, "fonts/all2em.ttf"), 152 Typeface.createFromAsset(assets, "fonts/hasGlyphTestFont.ttf"), 153 Typeface.createFromAsset(assets, "fonts/samplefont1.ttf"), 154 Typeface.createFromAsset(assets, "fonts/no_coverage.ttf"), 155 }; 156 157 final int loopCount = 10000; 158 159 final Runnable threadedCreater = () -> { 160 final Random random = new Random(); 161 for (int i = 0; i < loopCount; ++i) { 162 final Typeface base = baseTypefaces[random.nextInt(baseTypefaces.length)]; 163 if (random.nextBoolean()) { 164 final int style = random.nextInt(3); 165 final Typeface result = Typeface.create(base, style); 166 assertEquals(style, result.getStyle()); 167 } else { 168 final int weight = 100 * (random.nextInt(10) + 1); // [100, 1000] 169 final boolean italic = random.nextBoolean(); 170 final Typeface result = Typeface.create(base, weight, italic); 171 assertEquals(italic, result.isItalic()); 172 assertEquals(weight, result.getWeight()); 173 } 174 } 175 }; 176 177 final int threadCount = 4; 178 final Thread[] threads = new Thread[threadCount]; 179 for (int i = 0; i < threadCount; ++i) { 180 threads[i] = new Thread(threadedCreater); 181 } 182 183 for (int i = 0; i < threadCount; ++i) { 184 threads[i].start(); 185 } 186 187 for (int i = 0; i < threadCount; ++i) { 188 try { 189 threads[i].join(); 190 } catch (InterruptedException e) { 191 // ignore 192 } 193 } 194 195 } 196 197 @SmallTest 198 @Test testSerialize()199 public void testSerialize() throws Exception { 200 FontConfig fontConfig = SystemFonts.getSystemPreinstalledFontConfig(); 201 Map<String, FontFamily[]> fallbackMap = SystemFonts.buildSystemFallback(fontConfig); 202 Map<String, Typeface> systemFontMap = SystemFonts.buildSystemTypefaces(fontConfig, 203 fallbackMap); 204 SharedMemory sharedMemory = Typeface.serializeFontMap(systemFontMap); 205 Map<String, Typeface> copiedFontMap = new ArrayMap<>(); 206 try { 207 Typeface.deserializeFontMap(sharedMemory.mapReadOnly().order(ByteOrder.BIG_ENDIAN), 208 copiedFontMap); 209 assertEquals(systemFontMap.size(), copiedFontMap.size()); 210 for (String key : systemFontMap.keySet()) { 211 assertTrue(copiedFontMap.containsKey(key)); 212 Typeface original = systemFontMap.get(key); 213 Typeface copied = copiedFontMap.get(key); 214 assertEquals(original.getStyle(), copied.getStyle()); 215 assertEquals(original.getWeight(), copied.getWeight()); 216 assertEquals(measureText(original, "hello"), measureText(copied, "hello"), 1e-6); 217 } 218 } finally { 219 for (Typeface typeface : copiedFontMap.values()) { 220 typeface.releaseNativeObjectForTest(); 221 } 222 } 223 } 224 225 @SmallTest 226 @Test testSetSystemFontMap()227 public void testSetSystemFontMap() throws Exception { 228 229 // Typeface.setSystemFontMap mutate the returned map. So copying for the backup. 230 HashMap<String, Typeface> backup = new HashMap<>(Typeface.getSystemFontMap()); 231 232 Context context = InstrumentationRegistry.getInstrumentation().getTargetContext(); 233 Resources res = context.getResources(); 234 Map<String, Typeface> fontMap = Map.of( 235 "sans-serif", Typeface.create(res.getFont(R.font.samplefont), Typeface.NORMAL), 236 "serif", Typeface.create(res.getFont(R.font.samplefont2), Typeface.NORMAL), 237 "monospace", Typeface.create(res.getFont(R.font.samplefont3), Typeface.NORMAL), 238 "sample", Typeface.create(res.getFont(R.font.samplefont4), Typeface.NORMAL), 239 "sample-italic", Typeface.create(res.getFont(R.font.samplefont4), Typeface.ITALIC)); 240 241 try { 242 Typeface.setSystemFontMap(fontMap); 243 244 // Test public static final fields 245 assertEquals(fontMap.get("sans-serif"), Typeface.DEFAULT); 246 assertEquals(Typeface.BOLD, Typeface.DEFAULT_BOLD.getStyle()); 247 assertEquals(fontMap.get("sans-serif"), Typeface.SANS_SERIF); 248 assertEquals(fontMap.get("serif"), Typeface.SERIF); 249 assertEquals(fontMap.get("monospace"), Typeface.MONOSPACE); 250 251 // Test defaults 252 assertEquals(fontMap.get("sans-serif"), Typeface.defaultFromStyle(Typeface.NORMAL)); 253 for (int style : STYLES) { 254 String msg = "style = " + style; 255 assertNotNull(msg, Typeface.defaultFromStyle(style)); 256 assertEquals(msg, style, Typeface.defaultFromStyle(style).getStyle()); 257 } 258 259 // Test create() 260 assertEquals(fontMap.get("sample"), Typeface.create("sample", Typeface.NORMAL)); 261 assertEquals( 262 fontMap.get("sample-italic"), 263 Typeface.create("sample-italic", Typeface.ITALIC)); 264 } finally { 265 // This tests breaks many default font configuration and break the assumption of the 266 // subsequent test cases. To recover the original configuration, call the 267 // setSystemFontMap function with the original data even if it is a test target. 268 // Ideally, this test should be isolated and app should be restart after this test 269 // been executed. 270 Typeface.setSystemFontMap(backup); 271 } 272 } 273 measureText(Typeface typeface, String text)274 private static float measureText(Typeface typeface, String text) { 275 Paint paint = new Paint(); 276 paint.setTypeface(typeface); 277 return paint.measureText(text); 278 } 279 } 280