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 
22 import static org.mockito.ArgumentMatchers.any;
23 import static org.mockito.ArgumentMatchers.anyInt;
24 import static org.mockito.ArgumentMatchers.eq;
25 import static org.mockito.Mockito.doReturn;
26 import static org.mockito.Mockito.verify;
27 
28 import android.content.pm.ApplicationInfo;
29 import android.content.pm.PackageManager;
30 import android.content.res.Configuration;
31 
32 import androidx.test.ext.junit.runners.AndroidJUnit4;
33 
34 import com.google.common.collect.Maps;
35 
36 import org.junit.Before;
37 import org.junit.Rule;
38 import org.junit.Test;
39 import org.junit.runner.RunWith;
40 import org.mockito.Mock;
41 import org.mockito.junit.MockitoJUnit;
42 import org.mockito.junit.MockitoRule;
43 
44 import java.io.ByteArrayInputStream;
45 import java.io.ByteArrayOutputStream;
46 import java.io.IOException;
47 import java.io.ObjectInputStream;
48 import java.io.ObjectOutputStream;
49 import java.util.HashMap;
50 import java.util.List;
51 
52 @RunWith(AndroidJUnit4.class)
53 public class GrammaticalInflectionBackupTest {
54     private static final int DEFAULT_USER_ID = 0;
55     private static final String DEFAULT_PACKAGE_NAME = "com.test.package.name";
56 
57     @Rule
58     public final MockitoRule mockito = MockitoJUnit.rule();
59 
60     @Mock
61     private PackageManager mMockPackageManager;
62     @Mock
63     private GrammaticalInflectionService mGrammaticalInflectionService;
64 
65     private GrammaticalInflectionBackupHelper mBackupHelper;
66 
67     @Before
setUp()68     public void setUp() throws Exception {
69         mBackupHelper = new GrammaticalInflectionBackupHelper(
70                 mGrammaticalInflectionService, mMockPackageManager);
71     }
72 
73     @Test
testBackupPayload_noAppsInstalled_returnsNull()74     public void testBackupPayload_noAppsInstalled_returnsNull() {
75         assertNull(mBackupHelper.getBackupPayload(DEFAULT_USER_ID));
76     }
77 
78     @Test
testBackupPayload_AppsInstalled_returnsGender()79     public void testBackupPayload_AppsInstalled_returnsGender()
80             throws IOException, ClassNotFoundException {
81         mockAppInstalled();
82         mockGetApplicationGrammaticalGender(Configuration.GRAMMATICAL_GENDER_MASCULINE);
83 
84         HashMap<String, Integer> payload =
85                 readFromByteArray(mBackupHelper.getBackupPayload(DEFAULT_USER_ID));
86 
87         // verify the payload
88         HashMap<String, Integer> expectationMap = new HashMap<>();
89         expectationMap.put(DEFAULT_PACKAGE_NAME, Configuration.GRAMMATICAL_GENDER_MASCULINE);
90         assertTrue(Maps.difference(payload, expectationMap).areEqual());
91     }
92 
93     @Test
testApplyPayload_onPackageAdded_setApplicationGrammaticalGender()94     public void testApplyPayload_onPackageAdded_setApplicationGrammaticalGender()
95             throws IOException {
96         mockAppInstalled();
97 
98         HashMap<String, Integer> testData = new HashMap<>();
99         testData.put(DEFAULT_PACKAGE_NAME, Configuration.GRAMMATICAL_GENDER_NEUTRAL);
100         mBackupHelper.stageAndApplyRestoredPayload(convertToByteArray(testData), DEFAULT_USER_ID);
101         mBackupHelper.onPackageAdded(DEFAULT_PACKAGE_NAME, DEFAULT_USER_ID);
102 
103         verify(mGrammaticalInflectionService).setRequestedApplicationGrammaticalGender(
104                 eq(DEFAULT_PACKAGE_NAME),
105                 eq(DEFAULT_USER_ID),
106                 eq(Configuration.GRAMMATICAL_GENDER_NEUTRAL));
107     }
108 
mockAppInstalled()109     private void mockAppInstalled() {
110         ApplicationInfo dummyApp = new ApplicationInfo();
111         dummyApp.packageName = DEFAULT_PACKAGE_NAME;
112         doReturn(List.of(dummyApp)).when(mMockPackageManager)
113                 .getInstalledApplicationsAsUser(any(), anyInt());
114     }
115 
mockGetApplicationGrammaticalGender(int grammaticalGender)116     private void mockGetApplicationGrammaticalGender(int grammaticalGender) {
117         doReturn(grammaticalGender).when(mGrammaticalInflectionService)
118                 .getApplicationGrammaticalGender(
119                         eq(DEFAULT_PACKAGE_NAME), eq(DEFAULT_USER_ID));
120     }
121 
convertToByteArray(HashMap<String, Integer> pkgGenderInfo)122     private byte[] convertToByteArray(HashMap<String, Integer> pkgGenderInfo) throws IOException{
123         try (final ByteArrayOutputStream out = new ByteArrayOutputStream();
124              final ObjectOutputStream objStream = new ObjectOutputStream(out)) {
125             objStream.writeObject(pkgGenderInfo);
126             return out.toByteArray();
127         } catch (IOException e) {
128             throw e;
129         }
130     }
131 
readFromByteArray(byte[] payload)132     private HashMap<String, Integer> readFromByteArray(byte[] payload)
133             throws IOException, ClassNotFoundException {
134         HashMap<String, Integer> data;
135 
136         try (ByteArrayInputStream byteIn = new ByteArrayInputStream(payload);
137              ObjectInputStream in = new ObjectInputStream(byteIn)) {
138             data = (HashMap<String, Integer>) in.readObject();
139         } catch (IOException | ClassNotFoundException e) {
140             throw e;
141         }
142         return data;
143     }
144 }
145