diff options
Diffstat (limited to 'adservices/tests/unittest/service-core/src/com/android/adservices/service/topics/CacheManagerTest.java')
-rw-r--r-- | adservices/tests/unittest/service-core/src/com/android/adservices/service/topics/CacheManagerTest.java | 258 |
1 files changed, 238 insertions, 20 deletions
diff --git a/adservices/tests/unittest/service-core/src/com/android/adservices/service/topics/CacheManagerTest.java b/adservices/tests/unittest/service-core/src/com/android/adservices/service/topics/CacheManagerTest.java index 0eda2af514..ef39584e4a 100644 --- a/adservices/tests/unittest/service-core/src/com/android/adservices/service/topics/CacheManagerTest.java +++ b/adservices/tests/unittest/service-core/src/com/android/adservices/service/topics/CacheManagerTest.java @@ -17,6 +17,8 @@ package com.android.adservices.service.topics; import static com.google.common.truth.Truth.assertThat; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -33,9 +35,13 @@ import com.android.adservices.data.topics.Topic; import com.android.adservices.data.topics.TopicsDao; import com.android.adservices.data.topics.TopicsTables; import com.android.adservices.service.Flags; +import com.android.adservices.service.stats.AdServicesLogger; +import com.android.adservices.service.stats.GetTopicsReportedStats; + import org.junit.Before; import org.junit.Test; +import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; @@ -62,6 +68,7 @@ public final class CacheManagerTest { @Mock Flags mMockFlags; @Mock EpochManager mMockEpochManager; + @Mock AdServicesLogger mLogger; @Before public void setup() { @@ -76,34 +83,40 @@ public final class CacheManagerTest { DbTestUtil.deleteTable(TopicsTables.UsageHistoryContract.TABLE); DbTestUtil.deleteTable(TopicsTables.AppUsageHistoryContract.TABLE); DbTestUtil.deleteTable(TopicsTables.BlockedTopicsContract.TABLE); + DbTestUtil.deleteTable(TopicsTables.TopicContributorsContract.TABLE); DbHelper dbHelper = DbTestUtil.getDbHelperForTest(); mTopicsDao = new TopicsDao(dbHelper); - - // Erase all existing data. - DbTestUtil.deleteTable(TopicsTables.TaxonomyContract.TABLE); - DbTestUtil.deleteTable(TopicsTables.AppClassificationTopicsContract.TABLE); - DbTestUtil.deleteTable(TopicsTables.CallerCanLearnTopicsContract.TABLE); - DbTestUtil.deleteTable(TopicsTables.TopTopicsContract.TABLE); - DbTestUtil.deleteTable(TopicsTables.ReturnedTopicContract.TABLE); - DbTestUtil.deleteTable(TopicsTables.UsageHistoryContract.TABLE); - DbTestUtil.deleteTable(TopicsTables.AppUsageHistoryContract.TABLE); } @Test public void testGetTopics_emptyCache() { // The cache is empty when first created. - CacheManager cacheManager = new CacheManager(mMockEpochManager, mTopicsDao, mMockFlags); + CacheManager cacheManager = + new CacheManager(mMockEpochManager, mTopicsDao, mMockFlags, mLogger); List<Topic> topics = cacheManager.getTopics(/* numberOfLookBackEpochs = */ 3, "app", "sdk"); assertThat(topics).isEmpty(); + + // Verify GetTopicsReportedStats created for logging. + verify(mLogger) + .logGetTopicsReportedStats( + eq( + GetTopicsReportedStats.builder() + .setFilteredBlockedTopicCount(0) + .setDuplicateTopicCount(0) + .setTopicIdsCount(0) + .build())); } @Test public void testGetTopics() { // The cache is empty. - CacheManager cacheManager = new CacheManager(mMockEpochManager, mTopicsDao, mMockFlags); + CacheManager cacheManager = + new CacheManager(mMockEpochManager, mTopicsDao, mMockFlags, mLogger); + ArgumentCaptor<GetTopicsReportedStats> argument = + ArgumentCaptor.forClass(GetTopicsReportedStats.class); // Assume the current epochId is 4L, we will load cache for returned topics in the last 3 // epochs: epochId in {3, 2, 1}. @@ -215,12 +228,27 @@ public final class CacheManagerTest { assertThat(cacheManager.getTopics(/* numberOfLookBackEpochs = */ 3, "app5", "sdk1")) .containsExactlyElementsIn(Arrays.asList(topic1, topic5)); + + // GetTopics is invoked 19 times. + verify(mLogger, times(19)).logGetTopicsReportedStats(argument.capture()); + assertThat(argument.getAllValues()).hasSize(19); + // Verify log for the first call. + assertThat(argument.getAllValues().get(0)) + .isEqualTo( + GetTopicsReportedStats.builder() + .setFilteredBlockedTopicCount(0) + .setDuplicateTopicCount(0) + .setTopicIdsCount(0) + .build()); } @Test public void testGetTopics_someTopicsBlocked() { // The cache is empty. - CacheManager cacheManager = new CacheManager(mMockEpochManager, mTopicsDao, mMockFlags); + CacheManager cacheManager = + new CacheManager(mMockEpochManager, mTopicsDao, mMockFlags, mLogger); + ArgumentCaptor<GetTopicsReportedStats> argument = + ArgumentCaptor.forClass(GetTopicsReportedStats.class); // Assume the current epochId is 4L, we will load cache for returned topics in the last 3 // epochs: epochId in {3, 2, 1}. @@ -339,6 +367,103 @@ public final class CacheManagerTest { assertThat(cacheManager.getTopics(/* numberOfLookBackEpochs = */ 3, "app5", "sdk1")) .containsExactlyElementsIn(Arrays.asList(topic1, topic5)); + + // GetTopics is invoked 19 times. + verify(mLogger, times(19)).logGetTopicsReportedStats(argument.capture()); + assertThat(argument.getAllValues()).hasSize(19); + // Verify log for the first call. + assertThat(argument.getAllValues().get(0)) + .isEqualTo( + GetTopicsReportedStats.builder() + .setFilteredBlockedTopicCount(0) + .setDuplicateTopicCount(0) + .setTopicIdsCount(0) + .build()); + } + + @Test + public void testGetTopics_verifyLogs() { + // The cache is empty. + CacheManager cacheManager = + new CacheManager(mMockEpochManager, mTopicsDao, mMockFlags, mLogger); + ArgumentCaptor<GetTopicsReportedStats> argument = + ArgumentCaptor.forClass(GetTopicsReportedStats.class); + + // 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); + + Topic topic1 = Topic.create(/* topic */ 1, /* taxonomyVersion */ 1L, /* modelVersion */ 1L); + Topic topic2 = Topic.create(/* topic */ 2, /* taxonomyVersion */ 1L, /* modelVersion */ 1L); + Topic topic3 = Topic.create(/* topic */ 3, /* taxonomyVersion */ 1L, /* modelVersion */ 1L); + + // EpochId 1 + Map<Pair<String, String>, Topic> returnedAppSdkTopicsMap1 = new HashMap<>(); + returnedAppSdkTopicsMap1.put(Pair.create("app1", ""), topic1); + returnedAppSdkTopicsMap1.put(Pair.create("app1", "sdk1"), topic1); + returnedAppSdkTopicsMap1.put(Pair.create("app1", "sdk2"), topic1); + + mTopicsDao.persistReturnedAppTopicsMap(/* epochId */ 1L, returnedAppSdkTopicsMap1); + + // EpochId 2 + Map<Pair<String, String>, Topic> returnedAppSdkTopicsMap2 = new HashMap<>(); + + returnedAppSdkTopicsMap2.put(Pair.create("app1", ""), topic2); + returnedAppSdkTopicsMap2.put(Pair.create("app1", "sdk1"), topic2); + returnedAppSdkTopicsMap2.put(Pair.create("app1", "sdk2"), topic2); + + mTopicsDao.persistReturnedAppTopicsMap(/* epochId */ 2L, returnedAppSdkTopicsMap2); + + // EpochId 3 + Map<Pair<String, String>, Topic> returnedAppSdkTopicsMap3 = new HashMap<>(); + + returnedAppSdkTopicsMap3.put(Pair.create("app1", ""), topic3); + returnedAppSdkTopicsMap3.put(Pair.create("app1", "sdk1"), topic2); + returnedAppSdkTopicsMap3.put(Pair.create("app1", "sdk2"), topic1); + + mTopicsDao.persistReturnedAppTopicsMap(/* epochId */ 3L, returnedAppSdkTopicsMap3); + + // block topic 2. + mTopicsDao.recordBlockedTopic(topic2); + + cacheManager.loadCache(); + + verify(mMockEpochManager).getCurrentEpochId(); + verify(mMockFlags).getTopicsNumberOfLookBackEpochs(); + + // Now look at epochId in [1,..,3] by setting numberOfLookBackEpochs = 3. + // Should return topic1, topic2 and topic3, but topic2 is blocked - so only topic1 and + // topic3 are expected. + assertThat(cacheManager.getTopics(/* numberOfLookBackEpochs = */ 3, "app1", "")) + .containsExactlyElementsIn(Arrays.asList(topic1, topic3)); + // Should return topic1 and topic2, but topic2 is blocked - so only topic1 is expected. + assertThat(cacheManager.getTopics(/* numberOfLookBackEpochs = */ 3, "app1", "sdk1")) + .containsExactlyElementsIn(Arrays.asList(topic1)); + // Should return topic1 and topic2, but topic2 is blocked - so only topic1 is expected. + assertThat(cacheManager.getTopics(/* numberOfLookBackEpochs = */ 3, "app1", "sdk2")) + .containsExactlyElementsIn(Arrays.asList(topic1)); + + // GetTopics is invoked 3 times. + verify(mLogger, times(3)).logGetTopicsReportedStats(argument.capture()); + assertThat(argument.getAllValues()).hasSize(3); + // Should return topic1, topic2 and topic3, but topic2 is blocked - so only topic1 and + // topic3 are expected. + assertThat(argument.getAllValues().get(0).getFilteredBlockedTopicCount()).isEqualTo(1); + assertThat(argument.getAllValues().get(0).getDuplicateTopicCount()).isEqualTo(0); + assertThat(argument.getAllValues().get(0).getTopicIdsCount()).isEqualTo(2); + // Should return topic1 and topic2, but topic2 is blocked 2 times - so only topic1 is + // expected. + assertThat(argument.getAllValues().get(1).getFilteredBlockedTopicCount()).isEqualTo(2); + assertThat(argument.getAllValues().get(1).getDuplicateTopicCount()).isEqualTo(0); + assertThat(argument.getAllValues().get(1).getTopicIdsCount()).isEqualTo(1); + // Should return topic1 and topic2, but topic2 is blocked - so only topic1 is expected. + // topic1 is deduplicated. + assertThat(argument.getAllValues().get(2).getFilteredBlockedTopicCount()).isEqualTo(1); + assertThat(argument.getAllValues().get(2).getDuplicateTopicCount()).isEqualTo(1); + assertThat(argument.getAllValues().get(2).getTopicIdsCount()).isEqualTo(1); } @Test @@ -371,7 +496,8 @@ public final class CacheManagerTest { }); // The cache is empty. - CacheManager cacheManager = new CacheManager(mMockEpochManager, mTopicsDao, mMockFlags); + CacheManager cacheManager = + new CacheManager(mMockEpochManager, mTopicsDao, mMockFlags, mLogger); // Assume the current epochId is 4L, we will load cache for returned topics in the last 3 // epochs: epochId in {3, 2, 1}. @@ -423,7 +549,10 @@ public final class CacheManagerTest { @Test public void testGetTopics_duplicateTopics() { // The cache is empty. - CacheManager cacheManager = new CacheManager(mMockEpochManager, mTopicsDao, mMockFlags); + CacheManager cacheManager = + new CacheManager(mMockEpochManager, mTopicsDao, mMockFlags, mLogger); + ArgumentCaptor<GetTopicsReportedStats> argument = + ArgumentCaptor.forClass(GetTopicsReportedStats.class); // Assume the current epochId is 4L, we will load cache for returned topics in the last 3 // epochs: epochId in {3, 2, 1}. @@ -497,6 +626,18 @@ public final class CacheManagerTest { .containsExactlyElementsIn(Arrays.asList(topic1, topic2)); assertThat(cacheManager.getTopics(/* numberOfLookBackEpochs = */ 3, "app1", "sdk2")) .containsExactlyElementsIn(Arrays.asList(topic1, topic2, topic3)); + + // GetTopics is invoked 9 times. + verify(mLogger, times(9)).logGetTopicsReportedStats(argument.capture()); + assertThat(argument.getAllValues()).hasSize(9); + // Verify log for the first call. + assertThat(argument.getAllValues().get(0)) + .isEqualTo( + GetTopicsReportedStats.builder() + .setFilteredBlockedTopicCount(0) + .setDuplicateTopicCount(0) + .setTopicIdsCount(1) + .build()); } // Currently SQLException is not thrown. This test needs to be uplifted after SQLException gets @@ -505,19 +646,31 @@ public final class CacheManagerTest { @Test public void testGetTopics_failToLoadFromDb() { // The cache is empty when first created. - CacheManager cacheManager = new CacheManager(mMockEpochManager, mTopicsDao, mMockFlags); + CacheManager cacheManager = + new CacheManager(mMockEpochManager, mTopicsDao, mMockFlags, mLogger); // Fail to load from DB will have empty cache. List<Topic> topics = cacheManager.getTopics(/* numberOfLookBackEpochs = */ 3, "app", "sdk"); assertThat(topics).isEmpty(); + + // Verify GetTopicsReportedStats created for logging. + verify(mLogger) + .logGetTopicsReportedStats( + eq( + GetTopicsReportedStats.builder() + .setFilteredBlockedTopicCount(0) + .setDuplicateTopicCount(0) + .setTopicIdsCount(0) + .build())); } @Test public void testDump() { // Trigger the dump to verify no crash - CacheManager cacheManager = new CacheManager(mMockEpochManager, mTopicsDao, mMockFlags); + CacheManager cacheManager = + new CacheManager(mMockEpochManager, mTopicsDao, mMockFlags, mLogger); PrintWriter printWriter = new PrintWriter(new Writer() { @Override @@ -542,7 +695,8 @@ public final class CacheManagerTest { @Test public void testGetKnownTopicsWithConsent_noBlockedTopics() { // The cache is empty. - CacheManager cacheManager = new CacheManager(mMockEpochManager, mTopicsDao, mMockFlags); + CacheManager cacheManager = + new CacheManager(mMockEpochManager, mTopicsDao, mMockFlags, mLogger); // Assume the current epochId is 4L, we will load cache for returned topics in the last 3 // epochs: epochId in {3, 2, 1}. @@ -586,9 +740,71 @@ public final class CacheManagerTest { } @Test + public void testGetTopicsInEpochRange() { + Topic topic1 = + Topic.create(/* topic */ 1, /* taxonomyVersion = */ 1L, /* modelVersion = */ 1L); + Topic topic2 = + Topic.create(/* topic */ 2, /* taxonomyVersion = */ 1L, /* modelVersion = */ 1L); + Topic topic3 = + Topic.create(/* topic */ 3, /* taxonomyVersion = */ 1L, /* modelVersion = */ 1L); + Topic topic4 = + Topic.create(/* topic */ 4, /* taxonomyVersion = */ 1L, /* modelVersion = */ 1L); + Topic topic5 = + Topic.create(/* topic */ 5, /* taxonomyVersion = */ 1L, /* modelVersion = */ 1L); + + String app1 = "app1"; + String app2 = "app2"; + String sdk = "sdk"; + Pair<String, String> app1Sdk = Pair.create(app1, sdk); + Pair<String, String> app2Sdk = Pair.create(app2, sdk); + long epoch1 = 1L; + long epoch2 = 2L; + long epoch3 = 3L; + + long currentEpochId = 4L; + when(mMockEpochManager.getCurrentEpochId()).thenReturn(currentEpochId); + // Mock Flags to make it independent of configuration + when(mMockFlags.getTopicsNumberOfLookBackEpochs()).thenReturn(3); + + // App1 has Topic1 in Epoch 1. Topic2 in Epoch 2, Topic3 in Epoch 3. + // App2 has Topic4 in Epoch 1, Topic5 in Epoch 2. + mTopicsDao.persistReturnedAppTopicsMap(epoch1, Map.of(app1Sdk, topic1)); + mTopicsDao.persistReturnedAppTopicsMap(epoch2, Map.of(app1Sdk, topic2)); + mTopicsDao.persistReturnedAppTopicsMap(epoch3, Map.of(app1Sdk, topic3)); + mTopicsDao.persistReturnedAppTopicsMap(epoch1, Map.of(app2Sdk, topic4)); + mTopicsDao.persistReturnedAppTopicsMap(epoch2, Map.of(app2Sdk, topic5)); + + CacheManager cacheManager = + new CacheManager(mMockEpochManager, mTopicsDao, mMockFlags, mLogger); + cacheManager.loadCache(); + + // App1 should have topic1/2/3 in epoch range [1, 3]. + assertThat( + cacheManager.getTopicsInEpochRange( + /* epochLowerBound */ 1, /* epochUpperBound */ 3, app1, sdk)) + .isEqualTo(List.of(topic1, topic2, topic3)); + + // App1 should have topic2/3 in epoch range [2, 3]. + assertThat( + cacheManager.getTopicsInEpochRange( + /* epochLowerBound */ 2, /* epochUpperBound */ 3, app1, sdk)) + .isEqualTo(List.of(topic2, topic3)); + + // App2 should have topic4/5 in epoch range [1, 3]. + assertThat( + cacheManager.getTopicsInEpochRange( + /* epochLowerBound */ 1, /* epochUpperBound */ 3, app2, sdk)) + .isEqualTo(List.of(topic4, topic5)); + + verify(mMockEpochManager).getCurrentEpochId(); + verify(mMockFlags).getTopicsNumberOfLookBackEpochs(); + } + + @Test public void testGetKnownTopicsWithConsent_blockSomeTopics() { // The cache is empty. - CacheManager cacheManager = new CacheManager(mMockEpochManager, mTopicsDao, mMockFlags); + CacheManager cacheManager = + new CacheManager(mMockEpochManager, mTopicsDao, mMockFlags, mLogger); // Assume the current epochId is 4L, we will load cache for returned topics in the last 3 // epochs: epochId in {3, 2, 1}. @@ -639,7 +855,8 @@ public final class CacheManagerTest { @Test public void testGetKnownTopicsWithConsent_blockAllTopics() { // The cache is empty. - CacheManager cacheManager = new CacheManager(mMockEpochManager, mTopicsDao, mMockFlags); + CacheManager cacheManager = + new CacheManager(mMockEpochManager, mTopicsDao, mMockFlags, mLogger); // Assume the current epochId is 4L, we will load cache for returned topics in the last 3 // epochs: epochId in {3, 2, 1}. @@ -692,7 +909,8 @@ public final class CacheManagerTest { @Test public void testClearAllTopicsData() { // The cache is empty. - CacheManager cacheManager = new CacheManager(mMockEpochManager, mTopicsDao, mMockFlags); + CacheManager cacheManager = + new CacheManager(mMockEpochManager, mTopicsDao, mMockFlags, mLogger); Topic topic1 = Topic.create(/* topic */ 1, /* taxonomyVersion = */ 1L, /* modelVersion = */ 1L); |