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 
17 package android.adservices.ondevicepersonalization;
18 
19 import static junit.framework.Assert.assertEquals;
20 
21 import static org.junit.Assert.assertThrows;
22 
23 import android.adservices.ondevicepersonalization.aidl.IDataAccessService;
24 import android.adservices.ondevicepersonalization.aidl.IDataAccessServiceCallback;
25 import android.content.ContentValues;
26 import android.os.Bundle;
27 import android.os.RemoteException;
28 
29 import androidx.test.ext.junit.runners.AndroidJUnit4;
30 import androidx.test.filters.SmallTest;
31 
32 import com.android.ondevicepersonalization.internal.util.OdpParceledListSlice;
33 
34 import org.junit.Before;
35 import org.junit.Test;
36 import org.junit.runner.RunWith;
37 
38 import java.time.Instant;
39 import java.util.ArrayList;
40 import java.util.List;
41 
42 /**
43  * Unit Tests of LogReader API.
44  */
45 @SmallTest
46 @RunWith(AndroidJUnit4.class)
47 public class LogReaderTest {
48 
49     LogReader mLogReader;
50 
51     @Before
setup()52     public void setup() {
53         mLogReader = new LogReader(
54                 IDataAccessService.Stub.asInterface(
55                         new LogReaderTest.LocalDataService()));
56     }
57 
58     @Test
testGetRequestsSuccess()59     public void testGetRequestsSuccess() {
60         List<RequestLogRecord> result = mLogReader.getRequests(
61                 Instant.ofEpochMilli(10), Instant.ofEpochMilli(100));
62         assertEquals(2, result.size());
63         assertEquals(1, result.get(0).getRows().size());
64         assertEquals((int) (result.get(0).getRows().get(0).getAsInteger("a")), 1);
65         assertEquals((int) (result.get(0).getRows().get(0).getAsInteger("b")), 1);
66         assertEquals(1, result.get(1).getRows().size());
67         assertEquals((int) (result.get(1).getRows().get(0).getAsInteger("a")), 1);
68         assertEquals((int) (result.get(1).getRows().get(0).getAsInteger("b")), 1);
69     }
70 
71     @Test
testGetRequestsNullTimeError()72     public void testGetRequestsNullTimeError() {
73         assertThrows(NullPointerException.class, () -> mLogReader.getRequests(
74                 null, Instant.ofEpochMilli(100)));
75         assertThrows(NullPointerException.class, () -> mLogReader.getRequests(
76                 Instant.ofEpochMilli(100), null));
77     }
78 
79     @Test
testGetRequestsError()80     public void testGetRequestsError() {
81         // Triggers an expected error in the mock service.
82         assertThrows(IllegalStateException.class, () -> mLogReader.getRequests(
83                 Instant.ofEpochMilli(7), Instant.ofEpochMilli(100)));
84     }
85 
86     @Test
testGetRequestsNegativeTimeError()87     public void testGetRequestsNegativeTimeError() {
88         assertThrows(IllegalArgumentException.class, () -> mLogReader.getRequests(
89                 Instant.ofEpochMilli(-1), Instant.ofEpochMilli(100)));
90     }
91 
92     @Test
testGetRequestsBadTimeRangeError()93     public void testGetRequestsBadTimeRangeError() {
94         assertThrows(IllegalArgumentException.class, () -> mLogReader.getRequests(
95                 Instant.ofEpochMilli(100), Instant.ofEpochMilli(100)));
96         assertThrows(IllegalArgumentException.class, () -> mLogReader.getRequests(
97                 Instant.ofEpochMilli(1000), Instant.ofEpochMilli(100)));
98     }
99 
100     @Test
testGetJoinedEventsSuccess()101     public void testGetJoinedEventsSuccess() {
102         List<EventLogRecord> result = mLogReader.getJoinedEvents(
103                 Instant.ofEpochMilli(10), Instant.ofEpochMilli(100));
104         assertEquals(2, result.size());
105         assertEquals(result.get(0).getTimeMillis(), 30);
106         assertEquals(result.get(0).getRequestLogRecord().getTimeMillis(), 20);
107         assertEquals(result.get(0).getType(), 1);
108         assertEquals((int) (result.get(0).getData().getAsInteger("a")), 1);
109         assertEquals((int) (result.get(0).getData().getAsInteger("b")), 1);
110         assertEquals(result.get(1).getTimeMillis(), 40);
111         assertEquals(result.get(1).getRequestLogRecord().getTimeMillis(), 30);
112         assertEquals(result.get(1).getType(), 2);
113         assertEquals((int) (result.get(1).getData().getAsInteger("a")), 1);
114         assertEquals((int) (result.get(1).getData().getAsInteger("b")), 1);
115     }
116 
117     @Test
testGetJoinedEventsError()118     public void testGetJoinedEventsError() {
119         // Triggers an expected error in the mock service.
120         assertThrows(IllegalStateException.class, () -> mLogReader.getJoinedEvents(
121                 Instant.ofEpochMilli(7), Instant.ofEpochMilli(100)));
122     }
123 
124     @Test
testGetJoinedEventsNullTimeError()125     public void testGetJoinedEventsNullTimeError() {
126         assertThrows(NullPointerException.class, () -> mLogReader.getJoinedEvents(
127                 null, Instant.ofEpochMilli(100)));
128         assertThrows(NullPointerException.class, () -> mLogReader.getJoinedEvents(
129                 Instant.ofEpochMilli(100), null));
130     }
131 
132     @Test
testGetJoinedEventsNegativeTimeError()133     public void testGetJoinedEventsNegativeTimeError() {
134         assertThrows(IllegalArgumentException.class, () -> mLogReader.getJoinedEvents(
135                 Instant.ofEpochMilli(-1), Instant.ofEpochMilli(100)));
136     }
137 
138     @Test
testGetJoinedEventsInputError()139     public void testGetJoinedEventsInputError() {
140         assertThrows(IllegalArgumentException.class, () -> mLogReader.getJoinedEvents(
141                 Instant.ofEpochMilli(100), Instant.ofEpochMilli(100)));
142         assertThrows(IllegalArgumentException.class, () -> mLogReader.getJoinedEvents(
143                 Instant.ofEpochMilli(1000), Instant.ofEpochMilli(100)));
144     }
145 
146     public static class LocalDataService extends IDataAccessService.Stub {
147 
LocalDataService()148         public LocalDataService() {
149         }
150 
151         @Override
onRequest( int operation, Bundle params, IDataAccessServiceCallback callback)152         public void onRequest(
153                 int operation,
154                 Bundle params,
155                 IDataAccessServiceCallback callback) {
156             if (operation == Constants.DATA_ACCESS_OP_GET_REQUESTS
157                     || operation == Constants.DATA_ACCESS_OP_GET_JOINED_EVENTS) {
158                 long[] timestamps = params.getLongArray(Constants.EXTRA_LOOKUP_KEYS);
159                 if (timestamps[0] == 7) {
160                     // Raise expected error.
161                     try {
162                         callback.onError(Constants.STATUS_INTERNAL_ERROR);
163                     } catch (RemoteException e) {
164                         // Ignored.
165                     }
166                     return;
167                 }
168 
169                 Bundle result = new Bundle();
170                 ContentValues values = new ContentValues();
171                 values.put("a", 1);
172                 values.put("b", 1);
173                 if (operation == Constants.DATA_ACCESS_OP_GET_REQUESTS) {
174                     List<RequestLogRecord> records = new ArrayList<>();
175                     records.add(new RequestLogRecord.Builder()
176                             .setRequestId(1)
177                             .addRow(values)
178                             .build());
179                     records.add(new RequestLogRecord.Builder()
180                             .setRequestId(2)
181                             .addRow(values)
182                             .build());
183                     result.putParcelable(Constants.EXTRA_RESULT,
184                             new OdpParceledListSlice<RequestLogRecord>(records));
185                 } else if (operation == Constants.DATA_ACCESS_OP_GET_JOINED_EVENTS) {
186                     List<EventLogRecord> records = new ArrayList<>();
187                     records.add(new EventLogRecord.Builder()
188                             .setType(1)
189                             .setTimeMillis(30)
190                             .setData(values)
191                             .setRequestLogRecord(new RequestLogRecord.Builder()
192                                     .setRequestId(0)
193                                     .addRow(values)
194                                     .setTimeMillis(20)
195                                     .build())
196                             .build());
197                     records.add(new EventLogRecord.Builder()
198                             .setType(2)
199                             .setTimeMillis(40)
200                             .setData(values)
201                             .setRequestLogRecord(new RequestLogRecord.Builder()
202                                     .setRequestId(0)
203                                     .addRow(values)
204                                     .setTimeMillis(30)
205                                     .build())
206                             .build());
207                     result.putParcelable(Constants.EXTRA_RESULT,
208                             new OdpParceledListSlice<EventLogRecord>(records));
209                 }
210                 try {
211                     callback.onSuccess(result);
212                 } catch (RemoteException e) {
213                     // Ignored.
214                 }
215             }
216         }
217 
218         @Override
logApiCallStats(int apiName, long latencyMillis, int responseCode)219         public void logApiCallStats(int apiName, long latencyMillis, int responseCode) {}
220     }
221 }
222