• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 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 
17 package android.adservices.ondevicepersonalization;
18 
19 import static com.google.common.truth.Truth.assertThat;
20 
21 import static org.junit.Assert.assertArrayEquals;
22 import static org.junit.Assert.assertNull;
23 import static org.junit.Assert.assertTrue;
24 
25 import android.adservices.ondevicepersonalization.aidl.IDataAccessService;
26 import android.adservices.ondevicepersonalization.aidl.IDataAccessServiceCallback;
27 import android.os.Bundle;
28 import android.os.RemoteException;
29 
30 import androidx.test.ext.junit.runners.AndroidJUnit4;
31 import androidx.test.filters.SmallTest;
32 
33 import com.android.ondevicepersonalization.internal.util.ByteArrayParceledSlice;
34 
35 import org.junit.Before;
36 import org.junit.Test;
37 import org.junit.runner.RunWith;
38 
39 import java.util.HashMap;
40 import java.util.HashSet;
41 import java.util.Objects;
42 import java.util.Set;
43 import java.util.concurrent.CountDownLatch;
44 import java.util.concurrent.TimeUnit;
45 
46 /**
47  * Unit Tests of LocalDataImpl API.
48  */
49 @SmallTest
50 @RunWith(AndroidJUnit4.class)
51 public class LocalDataTest {
52     MutableKeyValueStore mLocalData;
53     LocalDataService mLocalDataService;
54     private static final long TIMEOUT_MILLI = 5000;
55 
56     @Before
setup()57     public void setup() {
58         mLocalDataService = new LocalDataService();
59         mLocalData = new LocalDataImpl(IDataAccessService.Stub.asInterface(mLocalDataService));
60     }
61 
62     @Test
testLookupSuccess()63     public void testLookupSuccess() throws Exception {
64         assertArrayEquals(new byte[] {1, 2, 3}, mLocalData.get("a"));
65         assertArrayEquals(new byte[] {7, 8, 9}, mLocalData.get("c"));
66         assertNull(mLocalData.get("e"));
67 
68         assertTrue(
69                 "Failed to await countdown latch!",
70                 mLocalDataService.mLatch.await(TIMEOUT_MILLI, TimeUnit.MILLISECONDS));
71         assertThat(mLocalDataService.mResponseCode).isEqualTo(Constants.STATUS_SUCCESS);
72     }
73 
74     @Test
testLookupError()75     public void testLookupError() throws InterruptedException {
76         // Triggers an expected error in the mock service.
77         assertNull(mLocalData.get("z"));
78 
79         assertTrue(
80                 "Failed to await countdown latch!",
81                 mLocalDataService.mLatch.await(TIMEOUT_MILLI, TimeUnit.MILLISECONDS));
82         assertThat(mLocalDataService.mResponseCode).isEqualTo(Constants.STATUS_SERVICE_FAILED);
83     }
84 
85     @Test
testLookupEmptyResult()86     public void testLookupEmptyResult() throws Exception {
87         // Triggers an expected error in the mock service.
88         assertNull(mLocalData.get("empty"));
89 
90         assertTrue(
91                 "Failed to await countdown latch!",
92                 mLocalDataService.mLatch.await(TIMEOUT_MILLI, TimeUnit.MILLISECONDS));
93         assertThat(mLocalDataService.mResponseCode)
94                 .isEqualTo(Constants.STATUS_SUCCESS_EMPTY_RESULT);
95     }
96 
97     @Test
testKeysetSuccess()98     public void testKeysetSuccess() throws InterruptedException {
99         Set<String> expectedResult = new HashSet<>();
100         expectedResult.add("a");
101         expectedResult.add("b");
102         expectedResult.add("c");
103 
104         assertThat(expectedResult).isEqualTo(mLocalData.keySet());
105         assertTrue(
106                 "Failed to await countdown latch!",
107                 mLocalDataService.mLatch.await(TIMEOUT_MILLI, TimeUnit.MILLISECONDS));
108         assertThat(mLocalDataService.mResponseCode).isEqualTo(Constants.STATUS_SUCCESS);
109     }
110 
111     @Test
testPutSuccess()112     public void testPutSuccess() throws Exception {
113         assertArrayEquals(new byte[] {1, 2, 3}, mLocalData.put("a", new byte[10]));
114         assertNull(mLocalData.put("e", new byte[] {1, 2, 3}));
115         assertArrayEquals(new byte[] {1, 2, 3}, mLocalData.get("e"));
116 
117         assertTrue(
118                 "Failed to await countdown latch!",
119                 mLocalDataService.mLatch.await(TIMEOUT_MILLI, TimeUnit.MILLISECONDS));
120         assertThat(mLocalDataService.mResponseCode).isEqualTo(Constants.STATUS_SUCCESS);
121     }
122 
123     @Test
testPutError()124     public void testPutError() throws InterruptedException {
125         // Triggers an expected error in the mock service.
126         assertNull(mLocalData.put("z", new byte[10]));
127 
128         assertTrue(
129                 "Failed to await countdown latch!",
130                 mLocalDataService.mLatch.await(TIMEOUT_MILLI, TimeUnit.MILLISECONDS));
131         assertThat(mLocalDataService.mResponseCode).isEqualTo(Constants.STATUS_SERVICE_FAILED);
132     }
133 
134     @Test
testRemoveSuccess()135     public void testRemoveSuccess() throws Exception {
136         assertArrayEquals(new byte[] {1, 2, 3}, mLocalData.remove("a"));
137         assertNull(mLocalData.remove("e"));
138         assertNull(mLocalData.get("a"));
139 
140         assertTrue(
141                 "Failed to await countdown latch!",
142                 mLocalDataService.mLatch.await(TIMEOUT_MILLI, TimeUnit.MILLISECONDS));
143         assertThat(mLocalDataService.mResponseCode).isEqualTo(Constants.STATUS_SUCCESS);
144     }
145 
146     @Test
testRemoveError()147     public void testRemoveError() throws InterruptedException {
148         // Triggers an expected error in the mock service.
149         assertNull(mLocalData.remove("z"));
150 
151         assertTrue(
152                 "Failed to await countdown latch!",
153                 mLocalDataService.mLatch.await(TIMEOUT_MILLI, TimeUnit.MILLISECONDS));
154         assertThat(mLocalDataService.mResponseCode).isEqualTo(Constants.STATUS_SERVICE_FAILED);
155     }
156 
157     public static class LocalDataService extends IDataAccessService.Stub {
158         HashMap<String, byte[]> mContents = new HashMap<String, byte[]>();
159         int mResponseCode;
160         CountDownLatch mLatch = new CountDownLatch(1);
161 
LocalDataService()162         public LocalDataService() {
163             mContents.put("a", new byte[] {1, 2, 3});
164             mContents.put("b", new byte[] {4, 5, 6});
165             mContents.put("c", new byte[] {7, 8, 9});
166         }
167 
168         @Override
onRequest( int operation, Bundle params, IDataAccessServiceCallback callback)169         public void onRequest(
170                 int operation,
171                 Bundle params,
172                 IDataAccessServiceCallback callback) {
173 
174             if (operation == Constants.DATA_ACCESS_OP_LOCAL_DATA_KEYSET) {
175                 Bundle result = new Bundle();
176                 result.putSerializable(Constants.EXTRA_RESULT,
177                         new HashSet<>(mContents.keySet()));
178                 try {
179                     callback.onSuccess(result);
180                 } catch (RemoteException e) {
181                     // Ignored.
182                 }
183                 return;
184             }
185 
186             String key = params.getString(Constants.EXTRA_LOOKUP_KEYS);
187             Objects.requireNonNull(key);
188             if (key.equals("empty")) {
189                 // Raise expected error.
190                 try {
191                     callback.onSuccess(null);
192                 } catch (RemoteException e) {
193                     // Ignored.
194                 }
195                 return;
196             }
197             if (key.equals("z")) {
198                 // Raise expected error.
199                 try {
200                     callback.onError(Constants.STATUS_SERVICE_FAILED);
201                 } catch (RemoteException e) {
202                     // Ignored.
203                 }
204                 return;
205             }
206             ByteArrayParceledSlice parceledByteArray = params.getParcelable(
207                     Constants.EXTRA_VALUE, ByteArrayParceledSlice.class);
208             byte[] value = null;
209             if (parceledByteArray != null) {
210                 value = parceledByteArray.getByteArray();
211             }
212 
213             if (operation == Constants.DATA_ACCESS_OP_LOCAL_DATA_LOOKUP
214                     || operation == Constants.DATA_ACCESS_OP_LOCAL_DATA_PUT
215                     || operation == Constants.DATA_ACCESS_OP_LOCAL_DATA_REMOVE) {
216                 byte[] existingValue = null;
217                 if (mContents.containsKey(key)) {
218                     existingValue = mContents.get(key);
219                 }
220                 if (operation == Constants.DATA_ACCESS_OP_LOCAL_DATA_REMOVE) {
221                     mContents.remove(key);
222                 }
223                 if (operation == Constants.DATA_ACCESS_OP_LOCAL_DATA_PUT) {
224                     mContents.put(key, value);
225                 }
226                 Bundle result = new Bundle();
227                 result.putParcelable(
228                         Constants.EXTRA_RESULT, new ByteArrayParceledSlice(existingValue));
229                 try {
230                     callback.onSuccess(result);
231                 } catch (RemoteException e) {
232                     // Ignored.
233                 }
234             }
235         }
236 
237         @Override
logApiCallStats(int apiName, long latencyMillis, int responseCode)238         public void logApiCallStats(int apiName, long latencyMillis, int responseCode) {
239             mLatch.countDown();
240             mResponseCode = responseCode;
241         }
242     }
243 }
244