1 /*
2  * Copyright (C) 2024 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.internal.inputmethod;
18 
19 import static org.junit.Assert.assertEquals;
20 import static org.junit.Assert.assertNotNull;
21 import static org.junit.Assert.assertNotSame;
22 import static org.junit.Assert.assertTrue;
23 
24 import android.annotation.NonNull;
25 import android.content.pm.ApplicationInfo;
26 import android.content.pm.ResolveInfo;
27 import android.content.pm.ServiceInfo;
28 import android.os.Parcel;
29 import android.platform.test.annotations.Presubmit;
30 import android.view.inputmethod.InputMethodInfo;
31 
32 import androidx.test.ext.junit.runners.AndroidJUnit4;
33 import androidx.test.filters.SmallTest;
34 
35 import org.junit.Test;
36 import org.junit.runner.RunWith;
37 
38 import java.util.ArrayList;
39 import java.util.Collections;
40 import java.util.List;
41 import java.util.function.Function;
42 
43 @SmallTest
44 @Presubmit
45 @RunWith(AndroidJUnit4.class)
46 public final class InputMethodInfoSafeListTest {
47 
48     @NonNull
createFakeInputMethodInfo(String packageName, String name)49     private static InputMethodInfo createFakeInputMethodInfo(String packageName, String name) {
50         final ResolveInfo ri = new ResolveInfo();
51         final ServiceInfo si = new ServiceInfo();
52         final ApplicationInfo ai = new ApplicationInfo();
53         ai.packageName = packageName;
54         ai.enabled = true;
55         ai.flags |= ApplicationInfo.FLAG_SYSTEM;
56         si.applicationInfo = ai;
57         si.enabled = true;
58         si.packageName = packageName;
59         si.name = name;
60         si.exported = true;
61         si.nonLocalizedLabel = name;
62         ri.serviceInfo = si;
63         return new InputMethodInfo(ri, false, "", Collections.emptyList(), 1, false);
64     }
65 
66     @NonNull
createTestInputMethodList()67     private static List<InputMethodInfo> createTestInputMethodList() {
68         final ArrayList<InputMethodInfo> list = new ArrayList<>();
69         list.add(createFakeInputMethodInfo("com.android.test.ime1", "TestIme1"));
70         list.add(createFakeInputMethodInfo("com.android.test.ime1", "TestIme2"));
71         list.add(createFakeInputMethodInfo("com.android.test.ime2", "TestIme"));
72         return list;
73     }
74 
75     @Test
testCreate()76     public void testCreate() {
77         assertNotNull(InputMethodInfoSafeList.create(createTestInputMethodList()));
78     }
79 
80     @Test
testExtract()81     public void testExtract() {
82         assertItemsAfterExtract(createTestInputMethodList(), InputMethodInfoSafeList::create);
83     }
84 
85     @Test
testExtractAfterParceling()86     public void testExtractAfterParceling() {
87         assertItemsAfterExtract(createTestInputMethodList(),
88                 originals -> cloneViaParcel(InputMethodInfoSafeList.create(originals)));
89     }
90 
91     @Test
testExtractEmptyList()92     public void testExtractEmptyList() {
93         assertItemsAfterExtract(Collections.emptyList(), InputMethodInfoSafeList::create);
94     }
95 
96     @Test
testExtractAfterParcelingEmptyList()97     public void testExtractAfterParcelingEmptyList() {
98         assertItemsAfterExtract(Collections.emptyList(),
99                 originals -> cloneViaParcel(InputMethodInfoSafeList.create(originals)));
100     }
101 
assertItemsAfterExtract(@onNull List<InputMethodInfo> originals, @NonNull Function<List<InputMethodInfo>, InputMethodInfoSafeList> factory)102     private static void assertItemsAfterExtract(@NonNull List<InputMethodInfo> originals,
103             @NonNull Function<List<InputMethodInfo>, InputMethodInfoSafeList> factory) {
104         final InputMethodInfoSafeList list = factory.apply(originals);
105         final List<InputMethodInfo> extracted = InputMethodInfoSafeList.extractFrom(list);
106         assertEquals(originals.size(), extracted.size());
107         for (int i = 0; i < originals.size(); ++i) {
108             assertNotSame("InputMethodInfoSafeList.extractFrom() must clone each instance",
109                     originals.get(i), extracted.get(i));
110             assertEquals("Verify the cloned instances have the equal value",
111                     originals.get(i).getPackageName(), extracted.get(i).getPackageName());
112         }
113 
114         // Subsequent calls of InputMethodInfoSafeList.extractFrom() return an empty list.
115         final List<InputMethodInfo> extracted2 = InputMethodInfoSafeList.extractFrom(list);
116         assertTrue(extracted2.isEmpty());
117     }
118 
119     @NonNull
cloneViaParcel( @onNull InputMethodInfoSafeList original)120     private static InputMethodInfoSafeList cloneViaParcel(
121             @NonNull InputMethodInfoSafeList original) {
122         Parcel parcel = null;
123         try {
124             parcel = Parcel.obtain();
125             original.writeToParcel(parcel, 0);
126             parcel.setDataPosition(0);
127             final InputMethodInfoSafeList newInstance =
128                     InputMethodInfoSafeList.CREATOR.createFromParcel(parcel);
129             assertNotNull(newInstance);
130             return newInstance;
131         } finally {
132             if (parcel != null) {
133                 parcel.recycle();
134             }
135         }
136     }
137 }
138