blob: f871c7dccecc51a87d628a073cb908d4ae9dc3d6 [file] [log] [blame]
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.adservices.service.topics;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.Context;
import android.util.Pair;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.filters.SmallTest;
import com.android.adservices.data.topics.Topic;
import com.android.adservices.data.topics.TopicsDao;
import com.android.adservices.service.Flags;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/** Unit tests for {@link com.android.adservices.service.topics.CacheManager} */
@SmallTest
public final class CacheManagerTest {
private static final String TAG = "CacheManagerTest";
private final Context mContext = ApplicationProvider.getApplicationContext();
private TopicsDao mTopicsDao;
@Mock Flags mMockFlags;
@Mock EpochManager mMockEpochManager;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
mTopicsDao = TopicsDao.getInstanceForTest(mContext);
}
@Test
public void testGetTopics_emptyCache() {
// The cache is empty when first created.
CacheManager cacheManager = new CacheManager(mMockEpochManager, mTopicsDao, mMockFlags);
List<Topic> topics = cacheManager.getTopics(
/* numberOfLookBackEpochs = */ 3,
"app", "sdk");
assertThat(topics).isEmpty();
}
@Test
public void testGetTopics() {
// The cache is empty.
CacheManager cacheManager = new CacheManager(mMockEpochManager, mTopicsDao, mMockFlags);
// Assume the current epochId is 4L, we will load cache for returned topics in the last 3
// epochs: epochId in {3, 2, 1}.
long currentEpochId = 4L;
when(mMockEpochManager.getCurrentEpochId()).thenReturn(currentEpochId);
// Mock Flags to make it independent of configuration
when(mMockFlags.getTopicsNumberOfLookBackEpochs()).thenReturn(3);
// EpochId 1
Map<Pair<String, String>, String> returnedAppSdkTopicsMap1 = new HashMap<>();
returnedAppSdkTopicsMap1.put(Pair.create("app1", ""), "topic1");
returnedAppSdkTopicsMap1.put(Pair.create("app1", "sdk1"), "topic1");
returnedAppSdkTopicsMap1.put(Pair.create("app1", "sdk2"), "topic1");
returnedAppSdkTopicsMap1.put(Pair.create("app2", "sdk1"), "topic2");
returnedAppSdkTopicsMap1.put(Pair.create("app2", "sdk3"), "topic2");
returnedAppSdkTopicsMap1.put(Pair.create("app2", "sdk4"), "topic2");
returnedAppSdkTopicsMap1.put(Pair.create("app3", "sdk1"), "topic3");
returnedAppSdkTopicsMap1.put(Pair.create("app5", "sdk1"), "topic5");
returnedAppSdkTopicsMap1.put(Pair.create("app5", "sdk5"), "topic5");
mTopicsDao.persistReturnedAppTopicsMap(/* epochId */ 1L,
/* taxonomyVersion */ 1L,
/* modelVersion */ 1L, returnedAppSdkTopicsMap1);
// EpochId 2
Map<Pair<String, String>, String> returnedAppSdkTopicsMap2 = new HashMap<>();
returnedAppSdkTopicsMap2.put(Pair.create("app1", ""), "topic2");
returnedAppSdkTopicsMap2.put(Pair.create("app1", "sdk1"), "topic2");
returnedAppSdkTopicsMap2.put(Pair.create("app1", "sdk2"), "topic2");
returnedAppSdkTopicsMap2.put(Pair.create("app2", "sdk1"), "topic3");
returnedAppSdkTopicsMap2.put(Pair.create("app2", "sdk3"), "topic3");
returnedAppSdkTopicsMap2.put(Pair.create("app2", "sdk4"), "topic3");
returnedAppSdkTopicsMap2.put(Pair.create("app3", "sdk1"), "topic4");
returnedAppSdkTopicsMap2.put(Pair.create("app5", "sdk1"), "topic1");
returnedAppSdkTopicsMap2.put(Pair.create("app5", "sdk5"), "topic1");
mTopicsDao.persistReturnedAppTopicsMap(/* epochId */ 2L,
/* taxonomyVersion */ 1L,
/* modelVersion */ 1L, returnedAppSdkTopicsMap2);
// EpochId 3
// epochId == 3 does not have any topics. This could happen if the epoch computation failed
// or the device was offline and no epoch computation was done.
cacheManager.loadCache();
verify(mMockEpochManager).getCurrentEpochId();
verify(mMockFlags).getTopicsNumberOfLookBackEpochs();
Topic topic1 = new Topic("topic1", /* taxonomyVersion = */ 1L,
/* modelVersion = */ 1L);
Topic topic2 = new Topic("topic2", /* taxonomyVersion = */ 1L,
/* modelVersion = */ 1L);
Topic topic3 = new Topic("topic3", /* taxonomyVersion = */ 1L,
/* modelVersion = */ 1L);
Topic topic4 = new Topic("topic4", /* taxonomyVersion = */ 1L,
/* modelVersion = */ 1L);
Topic topic5 = new Topic("topic5", /* taxonomyVersion = */ 1L,
/* modelVersion = */ 1L);
// Now look at epochId == 3 only by setting numberOfLookBackEpochs == 1.
// Since the epochId 3 has empty cache, the results are always empty.
assertThat(cacheManager.getTopics(/* numberOfLookBackEpochs = */ 1,
"app1", "")).isEmpty();
assertThat(cacheManager.getTopics(/* numberOfLookBackEpochs = */ 1,
"app1", "sdk1")).isEmpty();
assertThat(cacheManager.getTopics(/* numberOfLookBackEpochs = */ 1,
"app1", "sdk2")).isEmpty();
assertThat(cacheManager.getTopics(/* numberOfLookBackEpochs = */ 1,
"app3", "sdk1")).isEmpty();
assertThat(cacheManager.getTopics(/* numberOfLookBackEpochs = */ 1,
"app4", "sdk1")).isEmpty();
assertThat(cacheManager.getTopics(/* numberOfLookBackEpochs = */ 1,
"app5", "sdk1")).isEmpty();
// Now look at epochId in {3, 2} only by setting numberOfLookBackEpochs = 2.
assertThat(cacheManager.getTopics(/* numberOfLookBackEpochs = */ 2,
"app1", "")).isEqualTo(Arrays.asList(topic2));
assertThat(cacheManager.getTopics(/* numberOfLookBackEpochs = */ 2,
"app1", "sdk1")).isEqualTo(Arrays.asList(topic2));
assertThat(cacheManager.getTopics(/* numberOfLookBackEpochs = */ 2,
"app1", "sdk2")).isEqualTo(Arrays.asList(topic2));
assertThat(cacheManager.getTopics(/* numberOfLookBackEpochs = */ 2,
"app3", "sdk1")).isEqualTo(Arrays.asList(topic4));
assertThat(cacheManager.getTopics(/* numberOfLookBackEpochs = */ 2,
"app4", "sdk1")).isEmpty();
assertThat(cacheManager.getTopics(/* numberOfLookBackEpochs = */ 2,
"app5", "sdk1")).isEqualTo(Arrays.asList(topic1));
assertThat(cacheManager.getTopics(/* numberOfLookBackEpochs = */ 2,
"app5", "sdk5")).isEqualTo(Arrays.asList(topic1));
// Now look at epochId in [1,..,3] by setting numberOfLookBackEpochs = 3.
assertThat(cacheManager.getTopics(
/* numberOfLookBackEpochs = */ 3,
"app1", "")).isEqualTo(Arrays.asList(topic2, topic1));
assertThat(cacheManager.getTopics(
/* numberOfLookBackEpochs = */ 3,
"app1", "sdk1")).isEqualTo(Arrays.asList(topic2, topic1));
assertThat(cacheManager.getTopics(
/* numberOfLookBackEpochs = */ 3,
"app1", "sdk2")).isEqualTo(Arrays.asList(topic2, topic1));
assertThat(cacheManager.getTopics(
/* numberOfLookBackEpochs = */ 3,
"app3", "sdk1")).isEqualTo(Arrays.asList(topic4, topic3));
assertThat(cacheManager.getTopics(
/* numberOfLookBackEpochs = */ 3,
"app4", "sdk1")).isEmpty();
assertThat(cacheManager.getTopics(
/* numberOfLookBackEpochs = */ 3,
"app5", "sdk1")).isEqualTo(Arrays.asList(topic1, topic5));
}
// Currently SQLException is not thrown. This test needs to be uplifted after SQLException gets
// handled.
// TODO(b/230669931): Handle SQLException.
@Test
public void testGetTopics_failToLoadFromDb() {
// The cache is empty when first created.
CacheManager cacheManager = new CacheManager(mMockEpochManager, mTopicsDao, mMockFlags);
// Fail to load from DB will have empty cache.
List<Topic> topics = cacheManager.getTopics(
/* numberOfLookBackEpochs = */ 3,
"app", "sdk");
assertThat(topics).isEmpty();
}
}