1 /*
2  * Copyright (C) 2023 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 package android.app.appsearch.safeparcel;
17 
18 import static com.google.common.truth.Truth.assertThat;
19 
20 import android.os.Bundle;
21 import android.os.Parcel;
22 import android.util.SparseArray;
23 import android.util.SparseBooleanArray;
24 import android.util.SparseIntArray;
25 import android.util.SparseLongArray;
26 
27 import androidx.test.ext.junit.runners.AndroidJUnit4;
28 
29 import org.junit.Test;
30 import org.junit.runner.RunWith;
31 
32 @RunWith(AndroidJUnit4.class)
33 public final class SafeParcelableSparseArrayTest {
34     private static final int TEST_ID = 42;
35     private static final String BUNDLE_KEY = "test_bundle_key";
36 
37     private final Parcel mParcel = Parcel.obtain();
38 
newTestBundle(int key)39     private static Bundle newTestBundle(int key) {
40         Bundle b = new Bundle();
41         b.putInt(BUNDLE_KEY, key);
42         return b;
43     }
44 
assertTestBundleEquals(Bundle expected, Bundle actual)45     private static void assertTestBundleEquals(Bundle expected, Bundle actual) {
46         assertThat(actual.getInt(BUNDLE_KEY)).isEqualTo(expected.getInt(BUNDLE_KEY));
47     }
48 
newBundleSparseArray(int[] keys)49     private static SparseArray<Bundle> newBundleSparseArray(int[] keys) {
50         SparseArray<Bundle> sparseArray = new SparseArray<>(keys.length);
51         for (int i = 0; i < keys.length; i++) {
52             sparseArray.put(keys[i], newTestBundle(keys[i]));
53         }
54         return sparseArray;
55     }
56 
57     @Test
testReadWriteBytesSparseArray()58     public void testReadWriteBytesSparseArray() {
59         // Create SparseArray.
60         SparseArray<byte[]> sparseArray = new SparseArray<>();
61         sparseArray.append(20, new byte[] {1});
62         sparseArray.append(200, new byte[] {2});
63 
64         SafeParcelWriter.writeByteArraySparseArray(
65                 mParcel, TEST_ID, sparseArray, /* writeNull= */ false);
66 
67         int header = prepareToReadFromParcel();
68         SparseArray<byte[]> readSparseArray =
69                 SafeParcelReader.createByteArraySparseArray(mParcel, header);
70 
71         assertByteArrayEquals(sparseArray, readSparseArray);
72     }
73 
74     @Test
testReadWriteBundleSparseArray()75     public void testReadWriteBundleSparseArray() {
76         // Create SparseArray.
77         int[] keys = new int[] {0, 5, 10, 300, 5000};
78         SparseArray<Bundle> sparseArray = newBundleSparseArray(keys);
79 
80         SafeParcelWriter.writeTypedSparseArray(
81                 mParcel, TEST_ID, sparseArray, /* writeNull= */ false);
82 
83         int header = prepareToReadFromParcel();
84         SparseArray<Bundle> readSparseArray =
85                 SafeParcelReader.createTypedSparseArray(mParcel, header, Bundle.CREATOR);
86 
87         assertTestBundleEquals(sparseArray, readSparseArray);
88     }
89 
create(T[] values)90     private static <T> SparseArray<T> create(T[] values) {
91         SparseArray<T> result = new SparseArray<>();
92         int key = 0;
93         for (T value : values) {
94             result.append(key, value);
95             key += 2;
96         }
97         return result;
98     }
99 
createSparseBooleanArray(boolean[] values)100     private static SparseBooleanArray createSparseBooleanArray(boolean[] values) {
101         SparseBooleanArray result = new SparseBooleanArray();
102         int key = 0;
103         for (boolean value : values) {
104             result.append(key, value);
105             key += 2;
106         }
107         return result;
108     }
109 
createSparseIntArray(int[] values)110     private static SparseIntArray createSparseIntArray(int[] values) {
111         SparseIntArray result = new SparseIntArray();
112         int key = 0;
113         for (int value : values) {
114             result.append(key, value);
115             key += 2;
116         }
117         return result;
118     }
119 
createSparseLongArray(long[] values)120     private static SparseLongArray createSparseLongArray(long[] values) {
121         SparseLongArray result = new SparseLongArray();
122         int key = 0;
123         for (long value : values) {
124             result.append(key, value);
125             key += 2;
126         }
127         return result;
128     }
129 
130     @Test
testReadWriteSafeParcelableWithSparseArray()131     public void testReadWriteSafeParcelableWithSparseArray() {
132         // Create SafeParcelable.
133         SparseBooleanArray booleans = createSparseBooleanArray(new boolean[] {false, true, true});
134         SparseIntArray integers = createSparseIntArray(new int[] {100, 1000});
135         SparseLongArray longs = createSparseLongArray(new long[] {1000L, 20L});
136         SparseArray<Float> floats = create(new Float[] {3.1f, 3.14f});
137         SparseArray<Double> doubles = create(new Double[] {3.14, 3.1});
138         SparseArray<String> strings = create(new String[] {"foo", "bar"});
139         SparseArray<Bundle> bundles = newBundleSparseArray(new int[] {3, 7, 11});
140         SparseArray<byte[]> bytes = create(new byte[][] {new byte[] {1}, new byte[] {42}});
141         TestSafeParcelableWithSparseArray val =
142                 new TestSafeParcelableWithSparseArray(
143                         booleans, integers, longs, floats, doubles, strings, bundles, bytes);
144 
145         SafeParcelWriter.writeParcelable(mParcel, TEST_ID, val, 0, false);
146 
147         int header = prepareToReadFromParcel();
148         TestSafeParcelableWithSparseArray readVal =
149                 SafeParcelReader.createParcelable(
150                         mParcel, header, TestSafeParcelableWithSparseArray.CREATOR);
151 
152         assertEquals(val.mBooleans, readVal.mBooleans);
153         assertEquals(val.mIntegers, readVal.mIntegers);
154         assertEquals(val.mLongs, readVal.mLongs);
155         assertEquals(val.mFloats, readVal.mFloats);
156         assertEquals(val.mDoubles, readVal.mDoubles);
157         assertEquals(val.mStrings, readVal.mStrings);
158         assertTestBundleEquals(val.mBundles, readVal.mBundles);
159         assertByteArrayEquals(val.mBytes, readVal.mBytes);
160     }
161 
assertEquals(SparseIntArray expected, SparseIntArray actual)162     private static void assertEquals(SparseIntArray expected, SparseIntArray actual) {
163         assertThat(actual.size()).isEqualTo(expected.size());
164         for (int i = 0; i < expected.size(); i++) {
165             assertThat(actual.keyAt(i)).isEqualTo(expected.keyAt(i));
166             assertThat(actual.valueAt(i)).isEqualTo(expected.valueAt(i));
167         }
168     }
169 
assertEquals(SparseLongArray expected, SparseLongArray actual)170     private static void assertEquals(SparseLongArray expected, SparseLongArray actual) {
171         assertThat(actual.size()).isEqualTo(expected.size());
172         for (int i = 0; i < expected.size(); i++) {
173             assertThat(actual.keyAt(i)).isEqualTo(expected.keyAt(i));
174             assertThat(actual.valueAt(i)).isEqualTo(expected.valueAt(i));
175         }
176     }
177 
assertEquals(SparseBooleanArray expected, SparseBooleanArray actual)178     private static void assertEquals(SparseBooleanArray expected, SparseBooleanArray actual) {
179         assertThat(actual.size()).isEqualTo(expected.size());
180         for (int i = 0; i < expected.size(); i++) {
181             assertThat(actual.keyAt(i)).isEqualTo(expected.keyAt(i));
182             assertThat(actual.valueAt(i)).isEqualTo(expected.valueAt(i));
183         }
184     }
185 
assertSizeEquals(SparseArray<T> expected, SparseArray<T> actual)186     private static <T> void assertSizeEquals(SparseArray<T> expected, SparseArray<T> actual) {
187         if (expected == null) {
188             assertThat(actual).isNull();
189             return;
190         }
191         assertThat(actual).isNotNull();
192         assertThat(actual.size()).isEqualTo(expected.size());
193     }
194 
assertEquals(SparseArray<T> expected, SparseArray<T> actual)195     private static <T> void assertEquals(SparseArray<T> expected, SparseArray<T> actual) {
196         assertSizeEquals(expected, actual);
197         for (int i = 0; i < expected.size(); i++) {
198             assertThat(actual.keyAt(i)).isEqualTo(expected.keyAt(i));
199             assertThat(actual.valueAt(i)).isEqualTo(expected.valueAt(i));
200         }
201     }
202 
assertByteArrayEquals( SparseArray<byte[]> expected, SparseArray<byte[]> actual)203     private static void assertByteArrayEquals(
204             SparseArray<byte[]> expected, SparseArray<byte[]> actual) {
205         assertSizeEquals(expected, actual);
206         for (int i = 0; i < expected.size(); i++) {
207             assertThat(actual.keyAt(i)).isEqualTo(expected.keyAt(i));
208             assertThat(actual.valueAt(i)).isEqualTo(expected.valueAt(i));
209         }
210     }
211 
assertTestBundleEquals( SparseArray<Bundle> expected, SparseArray<Bundle> actual)212     private static void assertTestBundleEquals(
213             SparseArray<Bundle> expected, SparseArray<Bundle> actual) {
214         assertSizeEquals(expected, actual);
215         for (int i = 0; i < expected.size(); i++) {
216             assertThat(actual.keyAt(i)).isEqualTo(expected.keyAt(i));
217             assertTestBundleEquals(expected.valueAt(i), actual.valueAt(i));
218         }
219     }
220 
221     // Reset the data position and return the header.
prepareToReadFromParcel()222     private int prepareToReadFromParcel() {
223         mParcel.setDataPosition(0);
224         return SafeParcelReader.readHeader(mParcel);
225     }
226 }
227