1/* 2 * Copyright (C) 2017 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 */ 16package android.support.v4.provider; 17 18import static android.support.v4.provider.FontsContractCompat.Columns.RESULT_CODE_FONT_NOT_FOUND; 19import static android.support.v4.provider.FontsContractCompat.Columns.RESULT_CODE_FONT_UNAVAILABLE; 20import static android.support.v4.provider.FontsContractCompat.Columns.RESULT_CODE_MALFORMED_QUERY; 21import static android.support.v4.provider.FontsContractCompat.Columns.RESULT_CODE_OK; 22 23import static org.junit.Assert.assertEquals; 24import static org.junit.Assert.assertNotNull; 25import static org.junit.Assert.assertNotSame; 26import static org.junit.Assert.assertNull; 27import static org.junit.Assert.assertTrue; 28import static org.junit.Assert.fail; 29import static org.mockito.Matchers.anyInt; 30import static org.mockito.Matchers.anyString; 31import static org.mockito.Mockito.mock; 32import static org.mockito.Mockito.when; 33 34import android.app.Instrumentation; 35import android.content.Context; 36import android.content.pm.ApplicationInfo; 37import android.content.pm.PackageInfo; 38import android.content.pm.PackageManager; 39import android.content.pm.PackageManager.NameNotFoundException; 40import android.content.pm.ProviderInfo; 41import android.content.pm.Signature; 42import android.graphics.Typeface; 43import android.support.test.InstrumentationRegistry; 44import android.support.test.filters.SmallTest; 45import android.support.test.runner.AndroidJUnit4; 46import android.support.v4.provider.FontsContractCompat.FontFamilyResult; 47import android.support.v4.provider.FontsContractCompat.FontInfo; 48import android.util.Base64; 49 50import org.junit.After; 51import org.junit.Before; 52import org.junit.Test; 53import org.junit.runner.RunWith; 54 55import java.util.ArrayList; 56import java.util.Arrays; 57import java.util.Collections; 58import java.util.List; 59 60/** 61 * Unit tests for {@link FontsContractCompat}. 62 */ 63@RunWith(AndroidJUnit4.class) 64@SmallTest 65public class FontsContractCompatTest { 66 private static final String AUTHORITY = "android.support.provider.fonts.font"; 67 private static final String PACKAGE = "android.support.compat.test"; 68 69 // Signature to be used for authentication to access content provider. 70 // In this test case, the content provider and consumer live in the same package, self package's 71 // signature works. 72 private static final List<List<byte[]>> SIGNATURE; 73 static { 74 final Context context = InstrumentationRegistry.getInstrumentation().getTargetContext(); 75 try { 76 PackageManager manager = context.getPackageManager(); 77 PackageInfo info = manager.getPackageInfo( 78 context.getPackageName(), PackageManager.GET_SIGNATURES); 79 ArrayList<byte[]> out = new ArrayList<>(); 80 for (Signature sig : info.signatures) { 81 out.add(sig.toByteArray()); 82 } 83 SIGNATURE = new ArrayList<>(); 84 SIGNATURE.add(out); 85 } catch (PackageManager.NameNotFoundException e) { 86 throw new RuntimeException(e); 87 } 88 } 89 90 private static final byte[] BYTE_ARRAY = 91 Base64.decode("e04fd020ea3a6910a2d808002b30", Base64.DEFAULT); 92 // Use a different instance to test byte array comparison 93 private static final byte[] BYTE_ARRAY_COPY = 94 Base64.decode("e04fd020ea3a6910a2d808002b30", Base64.DEFAULT); 95 private static final byte[] BYTE_ARRAY_2 = 96 Base64.decode("e04fd020ea3a6910a2d808002b32", Base64.DEFAULT); 97 private Instrumentation mInstrumentation; 98 private Context mContext; 99 100 @Before 101 public void setUp() throws Exception { 102 mInstrumentation = InstrumentationRegistry.getInstrumentation(); 103 mContext = mInstrumentation.getTargetContext(); 104 MockFontProvider.prepareFontFiles( 105 InstrumentationRegistry.getInstrumentation().getTargetContext()); 106 } 107 108 @After 109 public void tearDown() { 110 MockFontProvider.cleanUpFontFiles( 111 InstrumentationRegistry.getInstrumentation().getTargetContext()); 112 } 113 114 private static class TestCallback extends FontsContractCompat.FontRequestCallback { 115 private Typeface mTypeface; 116 117 private int mSuccessCallCount; 118 private int mFailedCallCount; 119 120 public void onTypefaceRetrieved(Typeface typeface) { 121 mTypeface = typeface; 122 mSuccessCallCount++; 123 } 124 125 public void onTypefaceRequestFailed(int reason) { 126 mFailedCallCount++; 127 } 128 129 public Typeface getTypeface() { 130 return mTypeface; 131 } 132 133 public int getSuccessCallCount() { 134 return mSuccessCallCount; 135 } 136 137 public int getFailedCallCount() { 138 return mFailedCallCount; 139 } 140 } 141 142 @Test 143 public void typefaceNotCacheTest() throws NameNotFoundException { 144 FontRequest request = new FontRequest( 145 AUTHORITY, PACKAGE, MockFontProvider.SINGLE_FONT_FAMILY_QUERY, SIGNATURE); 146 FontFamilyResult result = FontsContractCompat.fetchFonts( 147 mContext, null /* cancellation signal */, request); 148 assertEquals(FontFamilyResult.STATUS_OK, result.getStatusCode()); 149 Typeface typeface = FontsContractCompat.buildTypeface( 150 mContext, null /* cancellation signal */, result.getFonts()); 151 152 FontFamilyResult result2 = FontsContractCompat.fetchFonts( 153 mContext, null /* cancellation signal */, request); 154 assertEquals(FontFamilyResult.STATUS_OK, result2.getStatusCode()); 155 Typeface typeface2 = FontsContractCompat.buildTypeface( 156 mContext, null /* cancellation signal */, result2.getFonts()); 157 158 // Neither fetchFonts nor buildTypeface should cache the Typeface. 159 assertNotSame(typeface, typeface2); 160 } 161 162 @Test 163 public void testGetFontFromProvider_resultOK() { 164 FontRequest request = new FontRequest( 165 AUTHORITY, PACKAGE, MockFontProvider.SINGLE_FONT_FAMILY2_QUERY, SIGNATURE); 166 FontInfo[] fonts = FontsContractCompat.getFontFromProvider( 167 mContext, request, AUTHORITY, null); 168 assertNotNull(fonts); 169 assertEquals(1, fonts.length); 170 FontInfo font = fonts[0]; 171 assertEquals(0, font.getTtcIndex()); 172 assertEquals(700, font.getWeight()); 173 assertTrue(font.isItalic()); 174 assertNotNull(font.getUri()); 175 assertEquals(RESULT_CODE_OK, font.getResultCode()); 176 } 177 178 @Test 179 public void testGetFontFromProvider_providerDoesntReturnAllFields() { 180 FontRequest request = new FontRequest( 181 AUTHORITY, PACKAGE, MockFontProvider.MANDATORY_FIELDS_ONLY_QUERY, SIGNATURE); 182 FontInfo[] fonts = FontsContractCompat.getFontFromProvider( 183 mContext, request, AUTHORITY, null); 184 assertNotNull(fonts); 185 assertEquals(1, fonts.length); 186 FontInfo font = fonts[0]; 187 assertEquals(0, font.getTtcIndex()); 188 assertEquals(RESULT_CODE_OK, font.getResultCode()); 189 } 190 191 @Test 192 public void testGetFontFromProvider_resultFontNotFound() { 193 FontRequest request = new FontRequest( 194 AUTHORITY, PACKAGE, MockFontProvider.NOT_FOUND_QUERY, SIGNATURE); 195 FontInfo[] fonts = FontsContractCompat.getFontFromProvider( 196 mContext, request, AUTHORITY, null); 197 assertNotNull(fonts); 198 assertEquals(1, fonts.length); 199 FontInfo font = fonts[0]; 200 assertEquals(RESULT_CODE_FONT_NOT_FOUND, font.getResultCode()); 201 } 202 203 @Test 204 public void testGetFontFromProvider_resultFontUnavailable() { 205 FontRequest request = new FontRequest( 206 AUTHORITY, PACKAGE, MockFontProvider.UNAVAILABLE_QUERY, SIGNATURE); 207 FontInfo[] fonts = FontsContractCompat.getFontFromProvider( 208 mContext, request, AUTHORITY, null); 209 210 assertNotNull(fonts); 211 assertEquals(1, fonts.length); 212 FontInfo font = fonts[0]; 213 assertEquals(RESULT_CODE_FONT_UNAVAILABLE, font.getResultCode()); 214 } 215 216 @Test 217 public void testGetFontFromProvider_resultMalformedQuery() { 218 FontRequest request = new FontRequest( 219 AUTHORITY, PACKAGE, MockFontProvider.MALFORMED_QUERY, SIGNATURE); 220 FontInfo[] fonts = FontsContractCompat.getFontFromProvider( 221 mContext, request, AUTHORITY, null); 222 223 assertNotNull(fonts); 224 assertEquals(1, fonts.length); 225 FontInfo font = fonts[0]; 226 assertEquals(RESULT_CODE_MALFORMED_QUERY, font.getResultCode()); 227 } 228 229 @Test 230 public void testGetFontFromProvider_resultFontNotFoundSecondRow() { 231 FontRequest request = new FontRequest( 232 AUTHORITY, PACKAGE, MockFontProvider.NOT_FOUND_SECOND_QUERY, SIGNATURE); 233 FontInfo[] fonts = FontsContractCompat.getFontFromProvider( 234 mContext, request, AUTHORITY, null); 235 236 assertNotNull(fonts); 237 assertEquals(2, fonts.length); 238 239 FontInfo font = fonts[0]; 240 assertEquals(RESULT_CODE_OK, font.getResultCode()); 241 242 font = fonts[1]; 243 assertEquals(RESULT_CODE_FONT_NOT_FOUND, font.getResultCode()); 244 } 245 246 @Test 247 public void testGetFontFromProvider_resultFontNotFoundOtherRow() { 248 FontRequest request = new FontRequest( 249 AUTHORITY, PACKAGE, MockFontProvider.NOT_FOUND_THIRD_QUERY, SIGNATURE); 250 FontInfo[] fonts = FontsContractCompat.getFontFromProvider( 251 mContext, request, AUTHORITY, null); 252 253 assertNotNull(fonts); 254 assertEquals(3, fonts.length); 255 256 FontInfo font = fonts[0]; 257 assertEquals(RESULT_CODE_OK, font.getResultCode()); 258 259 font = fonts[1]; 260 assertEquals(RESULT_CODE_FONT_NOT_FOUND, font.getResultCode()); 261 262 font = fonts[2]; 263 assertEquals(RESULT_CODE_OK, font.getResultCode()); 264 } 265 266 public void testGetFontFromProvider_resultCodeIsNegativeNumber() { 267 FontRequest request = new FontRequest( 268 AUTHORITY, PACKAGE, MockFontProvider.NEGATIVE_ERROR_CODE_QUERY, SIGNATURE); 269 FontInfo[] fonts = FontsContractCompat.getFontFromProvider( 270 mContext, request, AUTHORITY, null); 271 272 273 assertNotNull(fonts); 274 assertEquals(1, fonts.length); 275 FontInfo font = fonts[0]; 276 assertEquals(RESULT_CODE_FONT_NOT_FOUND, font.getResultCode()); 277 } 278 279 @Test 280 public void testGetProvider_providerNotFound() { 281 PackageManager packageManager = mock(PackageManager.class); 282 when(packageManager.resolveContentProvider(anyString(), anyInt())).thenReturn(null); 283 284 FontRequest request = new FontRequest(AUTHORITY, PACKAGE, "query", SIGNATURE); 285 try { 286 FontsContractCompat.getProvider(packageManager, request, null); 287 fail(); 288 } catch (NameNotFoundException e) { 289 // pass 290 } 291 } 292 293 @Test 294 public void testGetProvider_noCerts() 295 throws PackageManager.NameNotFoundException { 296 PackageManager packageManager = mContext.getPackageManager(); 297 298 List<List<byte[]>> emptyList = Collections.emptyList(); 299 300 FontRequest request = new FontRequest(AUTHORITY, PACKAGE, "query", emptyList); 301 assertNull(FontsContractCompat.getProvider(packageManager, request, null)); 302 } 303 304 @Test 305 public void testGetProvider_wrongCerts() 306 throws PackageManager.NameNotFoundException { 307 PackageManager packageManager = mock(PackageManager.class); 308 setupPackageManager(packageManager); 309 310 byte[] wrongCert = Base64.decode("this is a wrong cert", Base64.DEFAULT); 311 List<byte[]> certList = Arrays.asList(wrongCert); 312 FontRequest requestWrongCerts = new FontRequest( 313 AUTHORITY, PACKAGE, "query", Arrays.asList(certList)); 314 315 assertNull(FontsContractCompat.getProvider(packageManager, requestWrongCerts, null)); 316 } 317 318 @Test 319 public void testGetProvider_correctCerts() 320 throws PackageManager.NameNotFoundException { 321 PackageManager packageManager = mock(PackageManager.class); 322 ProviderInfo info = setupPackageManager(packageManager); 323 324 List<byte[]> certList = Arrays.asList(BYTE_ARRAY); 325 FontRequest requestRightCerts = new FontRequest( 326 AUTHORITY, PACKAGE, "query", Arrays.asList(certList)); 327 ProviderInfo result = 328 FontsContractCompat.getProvider(packageManager, requestRightCerts, null); 329 330 assertEquals(info, result); 331 } 332 333 @Test 334 public void testGetProvider_moreCerts() 335 throws PackageManager.NameNotFoundException { 336 PackageManager packageManager = mock(PackageManager.class); 337 setupPackageManager(packageManager); 338 339 byte[] wrongCert = Base64.decode("this is a wrong cert", Base64.DEFAULT); 340 List<byte[]> certList = Arrays.asList(wrongCert, BYTE_ARRAY); 341 FontRequest requestRightCerts = new FontRequest( 342 AUTHORITY, PACKAGE, "query", Arrays.asList(certList)); 343 assertNull(FontsContractCompat.getProvider(packageManager, requestRightCerts, null)); 344 } 345 346 @Test 347 public void testGetProvider_duplicateCerts() 348 throws PackageManager.NameNotFoundException { 349 PackageManager packageManager = mock(PackageManager.class); 350 setupPackageManager(packageManager); 351 PackageInfo packageInfo = new PackageInfo(); 352 Signature signature = mock(Signature.class); 353 when(signature.toByteArray()).thenReturn(BYTE_ARRAY_COPY); 354 Signature signature2 = mock(Signature.class); 355 when(signature2.toByteArray()).thenReturn(BYTE_ARRAY_COPY); 356 packageInfo.packageName = PACKAGE; 357 packageInfo.signatures = new Signature[] { signature, signature2 }; 358 when(packageManager.getPackageInfo(anyString(), anyInt())).thenReturn(packageInfo); 359 360 // The provider has {BYTE_ARRAY_COPY, BYTE_ARRAY_COPY}, the request has 361 // {BYTE_ARRAY_2, BYTE_ARRAY_COPY}. 362 List<byte[]> certList = Arrays.asList(BYTE_ARRAY_2, BYTE_ARRAY_COPY); 363 FontRequest requestRightCerts = new FontRequest( 364 AUTHORITY, PACKAGE, "query", Arrays.asList(certList)); 365 assertNull(FontsContractCompat.getProvider(packageManager, requestRightCerts, null)); 366 } 367 368 @Test 369 public void testGetProvider_correctCertsSeveralSets() 370 throws PackageManager.NameNotFoundException { 371 PackageManager packageManager = mock(PackageManager.class); 372 ProviderInfo info = setupPackageManager(packageManager); 373 374 List<List<byte[]>> certList = new ArrayList<>(); 375 byte[] wrongCert = Base64.decode("this is a wrong cert", Base64.DEFAULT); 376 certList.add(Arrays.asList(wrongCert)); 377 certList.add(Arrays.asList(BYTE_ARRAY)); 378 FontRequest requestRightCerts = new FontRequest(AUTHORITY, PACKAGE, "query", certList); 379 ProviderInfo result = 380 FontsContractCompat.getProvider(packageManager, requestRightCerts, null); 381 382 assertEquals(info, result); 383 } 384 385 @Test 386 public void testGetProvider_wrongPackage() 387 throws PackageManager.NameNotFoundException { 388 PackageManager packageManager = mContext.getPackageManager(); 389 390 List<List<byte[]>> certList = new ArrayList<>(); 391 certList.add(Arrays.asList(BYTE_ARRAY)); 392 FontRequest requestRightCerts = new FontRequest( 393 AUTHORITY, "com.wrong.package.name", "query", certList); 394 try { 395 FontsContractCompat.getProvider(packageManager, requestRightCerts, null); 396 fail(); 397 } catch (NameNotFoundException e) { 398 // pass 399 } 400 } 401 402 private ProviderInfo setupPackageManager(PackageManager packageManager) 403 throws PackageManager.NameNotFoundException { 404 ProviderInfo info = new ProviderInfo(); 405 info.packageName = PACKAGE; 406 info.applicationInfo = new ApplicationInfo(); 407 when(packageManager.resolveContentProvider(anyString(), anyInt())).thenReturn(info); 408 PackageInfo packageInfo = new PackageInfo(); 409 Signature signature = mock(Signature.class); 410 when(signature.toByteArray()).thenReturn(BYTE_ARRAY_COPY); 411 packageInfo.packageName = PACKAGE; 412 packageInfo.signatures = new Signature[] { signature }; 413 when(packageManager.getPackageInfo(anyString(), anyInt())).thenReturn(packageInfo); 414 return info; 415 } 416} 417