summaryrefslogtreecommitdiff
path: root/adservices/tests/unittest/service-core/src/com/android/adservices/service/MaintenanceJobServiceTest.java
diff options
context:
space:
mode:
Diffstat (limited to 'adservices/tests/unittest/service-core/src/com/android/adservices/service/MaintenanceJobServiceTest.java')
-rw-r--r--adservices/tests/unittest/service-core/src/com/android/adservices/service/MaintenanceJobServiceTest.java277
1 files changed, 259 insertions, 18 deletions
diff --git a/adservices/tests/unittest/service-core/src/com/android/adservices/service/MaintenanceJobServiceTest.java b/adservices/tests/unittest/service-core/src/com/android/adservices/service/MaintenanceJobServiceTest.java
index d625836a6a..4513beb233 100644
--- a/adservices/tests/unittest/service-core/src/com/android/adservices/service/MaintenanceJobServiceTest.java
+++ b/adservices/tests/unittest/service-core/src/com/android/adservices/service/MaintenanceJobServiceTest.java
@@ -17,19 +17,22 @@
package com.android.adservices.service;
import static com.android.adservices.service.AdServicesConfig.MAINTENANCE_JOB_ID;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyLong;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.staticMockMarker;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.verifyNoMoreInteractions;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
-import static org.mockito.ArgumentMatchers.anyLong;
-import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.times;
import android.app.job.JobInfo;
import android.app.job.JobParameters;
@@ -37,8 +40,12 @@ import android.app.job.JobScheduler;
import android.content.ComponentName;
import android.content.Context;
+import androidx.room.Room;
import androidx.test.core.app.ApplicationProvider;
+import com.android.adservices.data.adselection.AdSelectionDatabase;
+import com.android.adservices.data.adselection.AdSelectionEntryDao;
+import com.android.adservices.service.common.FledgeMaintenanceTasksWorker;
import com.android.adservices.service.topics.AppUpdateManager;
import com.android.adservices.service.topics.BlockedTopicsManager;
import com.android.adservices.service.topics.CacheManager;
@@ -50,15 +57,19 @@ import com.android.dx.mockito.inline.extended.ExtendedMockito;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
+import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
import org.mockito.MockitoSession;
import org.mockito.Spy;
+import org.mockito.quality.Strictness;
/** Unit tests for {@link com.android.adservices.service.MaintenanceJobService} */
@SuppressWarnings("ConstantConditions")
public class MaintenanceJobServiceTest {
private static final int BACKGROUND_THREAD_TIMEOUT_MS = 5_000;
+ private static final long MAINTENANCE_JOB_PERIOD_MS = 10_000L;
+ private static final long MAINTENANCE_JOB_FLEX_MS = 1_000L;
+ private static final long CURRENT_EPOCH_ID = 1L;
private static final Context CONTEXT = ApplicationProvider.getApplicationContext();
private static final JobScheduler JOB_SCHEDULER = CONTEXT.getSystemService(JobScheduler.class);
private static final Flags TEST_FLAGS = FlagsFactory.getFlagsForTest();
@@ -74,10 +85,20 @@ public class MaintenanceJobServiceTest {
@Mock AppUpdateManager mMockAppUpdateManager;
@Mock JobParameters mMockJobParameters;
@Mock Flags mMockFlags;
+ @Mock JobScheduler mMockJobScheduler;
+ private AdSelectionEntryDao mAdSelectionEntryDao;
+ @Spy private FledgeMaintenanceTasksWorker mFledgeMaintenanceTasksWorkerSpy;
@Before
public void setup() {
- MockitoAnnotations.initMocks(this);
+ mAdSelectionEntryDao =
+ Room.inMemoryDatabaseBuilder(
+ ApplicationProvider.getApplicationContext(),
+ AdSelectionDatabase.class)
+ .build()
+ .adSelectionEntryDao();
+
+ mFledgeMaintenanceTasksWorkerSpy = new FledgeMaintenanceTasksWorker(mAdSelectionEntryDao);
// Start a mockitoSession to mock static method
mStaticMockSession =
@@ -85,6 +106,8 @@ public class MaintenanceJobServiceTest {
.spyStatic(MaintenanceJobService.class)
.spyStatic(TopicsWorker.class)
.spyStatic(FlagsFactory.class)
+ .initMocks(this)
+ .strictness(Strictness.LENIENT)
.startMocking();
// Mock JobScheduler invocation in EpochJobService
@@ -97,7 +120,9 @@ public class MaintenanceJobServiceTest {
@After
public void teardown() {
JOB_SCHEDULER.cancelAll();
- mStaticMockSession.finishMocking();
+ if (mStaticMockSession != null) {
+ mStaticMockSession.finishMocking();
+ }
}
@Test
@@ -109,9 +134,86 @@ public class MaintenanceJobServiceTest {
mBlockedTopicsManager,
mMockAppUpdateManager,
TEST_FLAGS);
+ // Killswitch is off.
+ doReturn(false).when(mMockFlags).getTopicsKillSwitch();
+ doReturn(false).when(mMockFlags).getFledgeSelectAdsKillSwitch();
+ doReturn(CURRENT_EPOCH_ID).when(mMockEpochManager).getCurrentEpochId();
+ doReturn(TEST_FLAGS.getAdSelectionExpirationWindowS())
+ .when(mMockFlags)
+ .getAdSelectionExpirationWindowS();
+ // Mock static method FlagsFactory.getFlags() to return Mock Flags.
+ ExtendedMockito.doReturn(mMockFlags).when(FlagsFactory::getFlags);
+
+ // Mock static method AppUpdateWorker.getInstance, let it return the local
+ // appUpdateWorker in order to get a test instance.
+ ExtendedMockito.doReturn(topicsWorker)
+ .when(() -> TopicsWorker.getInstance(any(Context.class)));
+
+ mSpyMaintenanceJobService.injectFledgeMaintenanceTasksWorker(
+ mFledgeMaintenanceTasksWorkerSpy);
+
+ mSpyMaintenanceJobService.onStartJob(mMockJobParameters);
+
+ // Grant some time to allow background thread to execute
+ Thread.sleep(BACKGROUND_THREAD_TIMEOUT_MS);
+
+ ExtendedMockito.verify(() -> TopicsWorker.getInstance(any(Context.class)));
+ verify(mMockAppUpdateManager)
+ .reconcileUninstalledApps(any(Context.class), eq(CURRENT_EPOCH_ID));
+ verify(mMockAppUpdateManager)
+ .reconcileInstalledApps(any(Context.class), /* currentEpochId */ anyLong());
+
+ // Ensure Fledge job was done
+ verify(mFledgeMaintenanceTasksWorkerSpy).clearExpiredAdSelectionData();
+ }
+
+ @Test
+ public void testOnStartJob_TopicsKillSwitchOn() throws InterruptedException {
+ // Killswitch is off.
+ doReturn(true).when(mMockFlags).getTopicsKillSwitch();
+ doReturn(false).when(mMockFlags).getFledgeSelectAdsKillSwitch();
+ doReturn(CURRENT_EPOCH_ID).when(mMockEpochManager).getCurrentEpochId();
+
+ // Mock static method FlagsFactory.getFlags() to return Mock Flags.
+ ExtendedMockito.doReturn(mMockFlags).when(FlagsFactory::getFlags);
+ doReturn(TEST_FLAGS.getAdSelectionExpirationWindowS())
+ .when(mMockFlags)
+ .getAdSelectionExpirationWindowS();
+
+ // Inject FledgeMaintenanceTasksWorker since the test can't get it the standard way
+ mSpyMaintenanceJobService.injectFledgeMaintenanceTasksWorker(
+ mFledgeMaintenanceTasksWorkerSpy);
+
+ mSpyMaintenanceJobService.onStartJob(mMockJobParameters);
+
+ // Grant some time to allow background thread to execute
+ Thread.sleep(BACKGROUND_THREAD_TIMEOUT_MS);
+
+ // Verify that topics job is not done
+ ExtendedMockito.verify(() -> TopicsWorker.getInstance(any(Context.class)), never());
+ verify(mMockAppUpdateManager, never())
+ .reconcileUninstalledApps(any(Context.class), eq(CURRENT_EPOCH_ID));
+ verify(mMockAppUpdateManager, never())
+ .reconcileInstalledApps(any(Context.class), /* currentEpochId */ anyLong());
+
+ // Verifying that FLEDGE job was done
+ verify(mFledgeMaintenanceTasksWorkerSpy).clearExpiredAdSelectionData();
+ }
+
+ @Test
+ public void testOnStartJob_SelectAdsKillSwitchOn() throws InterruptedException {
+ final TopicsWorker topicsWorker =
+ new TopicsWorker(
+ mMockEpochManager,
+ mMockCacheManager,
+ mBlockedTopicsManager,
+ mMockAppUpdateManager,
+ TEST_FLAGS);
// Killswitch is off.
doReturn(false).when(mMockFlags).getTopicsKillSwitch();
+ doReturn(true).when(mMockFlags).getFledgeSelectAdsKillSwitch();
+ doReturn(CURRENT_EPOCH_ID).when(mMockEpochManager).getCurrentEpochId();
// Mock static method FlagsFactory.getFlags() to return Mock Flags.
ExtendedMockito.doReturn(mMockFlags).when(FlagsFactory::getFlags);
@@ -127,29 +229,36 @@ public class MaintenanceJobServiceTest {
Thread.sleep(BACKGROUND_THREAD_TIMEOUT_MS);
ExtendedMockito.verify(() -> TopicsWorker.getInstance(any(Context.class)));
- verify(mMockAppUpdateManager).reconcileUninstalledApps(any(Context.class));
+ verify(mMockAppUpdateManager)
+ .reconcileUninstalledApps(any(Context.class), eq(CURRENT_EPOCH_ID));
verify(mMockAppUpdateManager)
.reconcileInstalledApps(any(Context.class), /* currentEpochId */ anyLong());
+ verify(mFledgeMaintenanceTasksWorkerSpy, never()).clearExpiredAdSelectionData();
}
@Test
- public void testOnStartJob_killSwitchOn() throws InterruptedException {
+ public void testOnStartJob_killSwitchOn() {
// Killswitch on.
doReturn(true).when(mMockFlags).getTopicsKillSwitch();
+ doReturn(true).when(mMockFlags).getFledgeSelectAdsKillSwitch();
// Mock static method FlagsFactory.getFlags() to return Mock Flags.
ExtendedMockito.doReturn(mMockFlags).when(FlagsFactory::getFlags);
doNothing().when(mSpyMaintenanceJobService).jobFinished(mMockJobParameters, false);
+ // Inject FledgeMaintenanceTasksWorker since the test can't get it the standard way
+ mSpyMaintenanceJobService.injectFledgeMaintenanceTasksWorker(
+ mFledgeMaintenanceTasksWorkerSpy);
+
// Schedule the job to assert after starting that the scheduled job has been cancelled
JobInfo existingJobInfo =
new JobInfo.Builder(
MAINTENANCE_JOB_ID,
new ComponentName(CONTEXT, EpochJobService.class))
.setRequiresCharging(true)
- .setPeriodic(
- /* maintenanceJobPeriodMs */ 10000, /* maintenanceJobFlexMs */ 1000)
+ .setPeriodic(MAINTENANCE_JOB_PERIOD_MS, MAINTENANCE_JOB_FLEX_MS)
+ .setPersisted(true)
.build();
JOB_SCHEDULER.schedule(existingJobInfo);
assertNotNull(JOB_SCHEDULER.getPendingJob(MAINTENANCE_JOB_ID));
@@ -161,25 +270,145 @@ public class MaintenanceJobServiceTest {
verify(mSpyMaintenanceJobService).jobFinished(mMockJobParameters, false);
verifyNoMoreInteractions(staticMockMarker(TopicsWorker.class));
+ verify(mFledgeMaintenanceTasksWorkerSpy, never()).clearExpiredAdSelectionData();
}
@Test
- public void testOnStartJob_globalKillswitchOverridesAll() throws InterruptedException {
- // Global Killswitch is on.
- doReturn(true).when(mMockFlags).getGlobalKillSwitch();
+ public void testOnStartJob_killSwitchOnDoesFledgeJobWhenTopicsJobThrowsException()
+ throws Exception {
+ final TopicsWorker topicsWorker =
+ new TopicsWorker(
+ mMockEpochManager,
+ mMockCacheManager,
+ mBlockedTopicsManager,
+ mMockAppUpdateManager,
+ TEST_FLAGS);
+ // Killswitch is off.
+ doReturn(false).when(mMockFlags).getTopicsKillSwitch();
+ doReturn(false).when(mMockFlags).getFledgeSelectAdsKillSwitch();
+ doReturn(CURRENT_EPOCH_ID).when(mMockEpochManager).getCurrentEpochId();
+ doReturn(TEST_FLAGS.getAdSelectionExpirationWindowS())
+ .when(mMockFlags)
+ .getAdSelectionExpirationWindowS();
+
+ // Mock static method FlagsFactory.getFlags() to return Mock Flags.
+ ExtendedMockito.doReturn(mMockFlags).when(FlagsFactory::getFlags);
+
+ // Mock static method AppUpdateWorker.getInstance, let it return the local
+ // appUpdateWorker in order to get a test instance.
+ ExtendedMockito.doReturn(topicsWorker)
+ .when(() -> TopicsWorker.getInstance(any(Context.class)));
+
+ // Inject FledgeMaintenanceTasksWorker since the test can't get it the standard way
+ mSpyMaintenanceJobService.injectFledgeMaintenanceTasksWorker(
+ mFledgeMaintenanceTasksWorkerSpy);
+
+ // Simulating a failure in Topics job
+ ExtendedMockito.doThrow(new IllegalStateException())
+ .when(mMockAppUpdateManager)
+ .reconcileUninstalledApps(any(Context.class), eq(CURRENT_EPOCH_ID));
- // Killswitch off.
+ mSpyMaintenanceJobService.onStartJob(mMockJobParameters);
+
+ // Grant some time to allow background thread to execute
+ Thread.sleep(BACKGROUND_THREAD_TIMEOUT_MS);
+
+ ExtendedMockito.verify(() -> TopicsWorker.getInstance(any(Context.class)));
+ verify(mMockAppUpdateManager)
+ .reconcileUninstalledApps(any(Context.class), eq(CURRENT_EPOCH_ID));
+ // Verify that this is not called because we threw an exception
+ verify(mMockAppUpdateManager, never())
+ .reconcileInstalledApps(any(Context.class), /* currentEpochId */ anyLong());
+
+ // Verifying that FLEDGE job was done
+ verify(mFledgeMaintenanceTasksWorkerSpy).clearExpiredAdSelectionData();
+ }
+
+ @Test
+ public void testOnStartJob_killSwitchOnDoesTopicsJobWhenFledgeThrowsException()
+ throws Exception {
+ final TopicsWorker topicsWorker =
+ new TopicsWorker(
+ mMockEpochManager,
+ mMockCacheManager,
+ mBlockedTopicsManager,
+ mMockAppUpdateManager,
+ TEST_FLAGS);
+ // Killswitch is off.
doReturn(false).when(mMockFlags).getTopicsKillSwitch();
+ doReturn(false).when(mMockFlags).getFledgeSelectAdsKillSwitch();
+ doReturn(CURRENT_EPOCH_ID).when(mMockEpochManager).getCurrentEpochId();
// Mock static method FlagsFactory.getFlags() to return Mock Flags.
ExtendedMockito.doReturn(mMockFlags).when(FlagsFactory::getFlags);
+ // Mock static method AppUpdateWorker.getInstance, let it return the local
+ // appUpdateWorker in order to get a test instance.
+ ExtendedMockito.doReturn(topicsWorker)
+ .when(() -> TopicsWorker.getInstance(any(Context.class)));
+
+ // Inject FledgeMaintenanceTasksWorker since the test can't get it the standard way
+ mSpyMaintenanceJobService.injectFledgeMaintenanceTasksWorker(
+ mFledgeMaintenanceTasksWorkerSpy);
+
+ // Simulating a failure in Fledge job
+ ExtendedMockito.doThrow(new IllegalStateException())
+ .when(mFledgeMaintenanceTasksWorkerSpy)
+ .clearExpiredAdSelectionData();
+
+ mSpyMaintenanceJobService.onStartJob(mMockJobParameters);
+
+ // Grant some time to allow background thread to execute
+ Thread.sleep(BACKGROUND_THREAD_TIMEOUT_MS);
+
+ ExtendedMockito.verify(() -> TopicsWorker.getInstance(any(Context.class)));
+ verify(mMockAppUpdateManager)
+ .reconcileUninstalledApps(any(Context.class), eq(CURRENT_EPOCH_ID));
+ verify(mMockAppUpdateManager)
+ .reconcileInstalledApps(any(Context.class), /* currentEpochId */ anyLong());
+
+ verify(mFledgeMaintenanceTasksWorkerSpy).clearExpiredAdSelectionData();
+ }
+
+ @Test
+ public void testOnStartJob_globalKillswitchOverridesAll() throws InterruptedException {
+ Flags flagsGlobalKillSwitchOn =
+ new Flags() {
+ @Override
+ public boolean getGlobalKillSwitch() {
+ return true;
+ }
+ };
+
+ // Setting mock flags to use flags with global switch overridden
+ doReturn(flagsGlobalKillSwitchOn.getTopicsKillSwitch())
+ .when(mMockFlags)
+ .getTopicsKillSwitch();
+ doReturn(flagsGlobalKillSwitchOn.getTopicsKillSwitch())
+ .when(mMockFlags)
+ .getFledgeSelectAdsKillSwitch();
+ doReturn(CURRENT_EPOCH_ID).when(mMockEpochManager).getCurrentEpochId();
+
+ // Mock static method FlagsFactory.getFlags() to return Mock Flags.
+ ExtendedMockito.doReturn(mMockFlags).when(FlagsFactory::getFlags);
+
+ ExtendedMockito.doNothing()
+ .when(mSpyMaintenanceJobService)
+ .jobFinished(mMockJobParameters, false);
+
mSpyMaintenanceJobService.onStartJob(mMockJobParameters);
// Grant some time to allow background thread to execute
Thread.sleep(BACKGROUND_THREAD_TIMEOUT_MS);
- // When the kill switch is on, the MaintenanceJobService exits early and do nothing.
+ verify(() -> TopicsWorker.getInstance(any(Context.class)), never());
+ verify(mMockAppUpdateManager, never())
+ .reconcileUninstalledApps(any(Context.class), eq(CURRENT_EPOCH_ID));
+ verify(mMockAppUpdateManager, never())
+ .reconcileInstalledApps(any(Context.class), /* currentEpochId */ anyLong());
+
+ // Ensure Fledge job not was done
+ verify(mFledgeMaintenanceTasksWorkerSpy, never()).clearExpiredAdSelectionData();
}
@Test
@@ -256,7 +485,7 @@ public class MaintenanceJobServiceTest {
}
@Test
- public void testScheduleIfNeeded_scheduledWithKillSwichOn() {
+ public void testScheduleIfNeeded_scheduledWithKillSwitchOn() {
// Killswitch is on.
doReturn(true).when(mMockFlags).getTopicsKillSwitch();
@@ -267,4 +496,16 @@ public class MaintenanceJobServiceTest {
assertThat(EpochJobService.scheduleIfNeeded(CONTEXT, /* forceSchedule */ false)).isFalse();
assertThat(JOB_SCHEDULER.getPendingJob(MAINTENANCE_JOB_ID)).isNull();
}
+
+ @Test
+ public void testSchedule_jobInfoIsPersisted() {
+ final ArgumentCaptor<JobInfo> argumentCaptor = ArgumentCaptor.forClass(JobInfo.class);
+
+ MaintenanceJobService.schedule(
+ CONTEXT, mMockJobScheduler, MAINTENANCE_JOB_PERIOD_MS, MAINTENANCE_JOB_FLEX_MS);
+
+ verify(mMockJobScheduler, times(1)).schedule(argumentCaptor.capture());
+ assertThat(argumentCaptor.getValue()).isNotNull();
+ assertThat(argumentCaptor.getValue().isPersisted()).isTrue();
+ }
}