1 /* 2 * Copyright (C) 2022 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 com.android.server.grammaticalinflection; 18 19 import static junit.framework.Assert.assertNull; 20 import static junit.framework.Assert.assertTrue; 21 import static junit.framework.Assert.assertEquals; 22 23 import static org.mockito.ArgumentMatchers.any; 24 import static org.mockito.ArgumentMatchers.anyInt; 25 import static org.mockito.ArgumentMatchers.eq; 26 import static org.mockito.Mockito.doReturn; 27 import static org.mockito.Mockito.verify; 28 29 import android.content.pm.ApplicationInfo; 30 import android.content.pm.PackageManager; 31 import android.content.res.Configuration; 32 33 import androidx.test.ext.junit.runners.AndroidJUnit4; 34 35 import com.google.common.collect.Maps; 36 37 import org.junit.Before; 38 import org.junit.Rule; 39 import org.junit.Test; 40 import org.junit.runner.RunWith; 41 import org.mockito.Mock; 42 import org.mockito.junit.MockitoJUnit; 43 import org.mockito.junit.MockitoRule; 44 45 import java.io.ByteArrayInputStream; 46 import java.io.ByteArrayOutputStream; 47 import java.io.IOException; 48 import java.io.ObjectInputStream; 49 import java.io.ObjectOutputStream; 50 import java.nio.ByteBuffer; 51 import java.util.HashMap; 52 import java.util.List; 53 54 @RunWith(AndroidJUnit4.class) 55 public class GrammaticalInflectionBackupTest { 56 private static final int DEFAULT_USER_ID = 0; 57 private static final String DEFAULT_PACKAGE_NAME = "com.test.package.name"; 58 59 @Rule 60 public final MockitoRule mockito = MockitoJUnit.rule(); 61 62 @Mock 63 private PackageManager mMockPackageManager; 64 @Mock 65 private GrammaticalInflectionService mGrammaticalInflectionService; 66 67 private GrammaticalInflectionBackupHelper mBackupHelper; 68 69 @Before setUp()70 public void setUp() throws Exception { 71 mBackupHelper = new GrammaticalInflectionBackupHelper( 72 null, mGrammaticalInflectionService, mMockPackageManager); 73 } 74 75 @Test testBackupPayload_noAppsInstalled_returnsNull()76 public void testBackupPayload_noAppsInstalled_returnsNull() { 77 assertNull(mBackupHelper.getBackupPayload(DEFAULT_USER_ID)); 78 } 79 80 @Test testBackupPayload_AppsInstalled_returnsGender()81 public void testBackupPayload_AppsInstalled_returnsGender() 82 throws IOException, ClassNotFoundException { 83 mockAppInstalled(); 84 mockGetApplicationGrammaticalGender(Configuration.GRAMMATICAL_GENDER_MASCULINE); 85 86 HashMap<String, Integer> payload = 87 readFromByteArray(mBackupHelper.getBackupPayload(DEFAULT_USER_ID)); 88 89 // verify the payload 90 HashMap<String, Integer> expectationMap = new HashMap<>(); 91 expectationMap.put(DEFAULT_PACKAGE_NAME, Configuration.GRAMMATICAL_GENDER_MASCULINE); 92 assertTrue(Maps.difference(payload, expectationMap).areEqual()); 93 } 94 95 @Test testApplyPayload_onPackageAdded_setApplicationGrammaticalGender()96 public void testApplyPayload_onPackageAdded_setApplicationGrammaticalGender() 97 throws IOException { 98 mockAppInstalled(); 99 100 HashMap<String, Integer> testData = new HashMap<>(); 101 testData.put(DEFAULT_PACKAGE_NAME, Configuration.GRAMMATICAL_GENDER_NEUTRAL); 102 mBackupHelper.stageAndApplyRestoredPayload(convertToByteArray(testData), DEFAULT_USER_ID); 103 mBackupHelper.onPackageAdded(DEFAULT_PACKAGE_NAME, DEFAULT_USER_ID); 104 105 verify(mGrammaticalInflectionService).setRequestedApplicationGrammaticalGender( 106 eq(DEFAULT_PACKAGE_NAME), 107 eq(DEFAULT_USER_ID), 108 eq(Configuration.GRAMMATICAL_GENDER_NEUTRAL)); 109 } 110 111 @Test testSystemBackupPayload_returnsGender()112 public void testSystemBackupPayload_returnsGender() 113 throws IOException, ClassNotFoundException { 114 doReturn(Configuration.GRAMMATICAL_GENDER_MASCULINE).when(mGrammaticalInflectionService) 115 .getSystemGrammaticalGender(eq(DEFAULT_USER_ID)); 116 117 int gender = convertByteArrayToInt(mBackupHelper.getSystemBackupPayload(DEFAULT_USER_ID)); 118 119 assertEquals(gender, Configuration.GRAMMATICAL_GENDER_MASCULINE); 120 } 121 122 @Test testApplySystemPayload_setSystemWideGrammaticalGender()123 public void testApplySystemPayload_setSystemWideGrammaticalGender() 124 throws IOException { 125 mBackupHelper.applyRestoredSystemPayload( 126 intToByteArray(Configuration.GRAMMATICAL_GENDER_NEUTRAL), DEFAULT_USER_ID); 127 128 verify(mGrammaticalInflectionService).setSystemWideGrammaticalGender( 129 eq(Configuration.GRAMMATICAL_GENDER_NEUTRAL), 130 eq(DEFAULT_USER_ID)); 131 } 132 mockAppInstalled()133 private void mockAppInstalled() { 134 ApplicationInfo dummyApp = new ApplicationInfo(); 135 dummyApp.packageName = DEFAULT_PACKAGE_NAME; 136 doReturn(List.of(dummyApp)).when(mMockPackageManager) 137 .getInstalledApplicationsAsUser(any(), anyInt()); 138 } 139 mockGetApplicationGrammaticalGender(int grammaticalGender)140 private void mockGetApplicationGrammaticalGender(int grammaticalGender) { 141 doReturn(grammaticalGender).when(mGrammaticalInflectionService) 142 .getApplicationGrammaticalGender( 143 eq(DEFAULT_PACKAGE_NAME), eq(DEFAULT_USER_ID)); 144 } 145 convertToByteArray(HashMap<String, Integer> pkgGenderInfo)146 private byte[] convertToByteArray(HashMap<String, Integer> pkgGenderInfo) throws IOException{ 147 try (final ByteArrayOutputStream out = new ByteArrayOutputStream(); 148 final ObjectOutputStream objStream = new ObjectOutputStream(out)) { 149 objStream.writeObject(pkgGenderInfo); 150 return out.toByteArray(); 151 } catch (IOException e) { 152 throw e; 153 } 154 } 155 readFromByteArray(byte[] payload)156 private HashMap<String, Integer> readFromByteArray(byte[] payload) 157 throws IOException, ClassNotFoundException { 158 HashMap<String, Integer> data; 159 160 try (ByteArrayInputStream byteIn = new ByteArrayInputStream(payload); 161 ObjectInputStream in = new ObjectInputStream(byteIn)) { 162 data = (HashMap<String, Integer>) in.readObject(); 163 } catch (IOException | ClassNotFoundException e) { 164 throw e; 165 } 166 return data; 167 } 168 intToByteArray(final int gender)169 private byte[] intToByteArray(final int gender) { 170 ByteBuffer bb = ByteBuffer.allocate(4); 171 bb.putInt(gender); 172 return bb.array(); 173 } 174 convertByteArrayToInt(byte[] intBytes)175 private int convertByteArrayToInt(byte[] intBytes) { 176 ByteBuffer byteBuffer = ByteBuffer.wrap(intBytes); 177 return byteBuffer.getInt(); 178 } 179 } 180