diff options
Diffstat (limited to 'adservices/tests/unittest/service-core/src/com/android/adservices/service/measurement/reporting/DebugReportingJobServiceTest.java')
-rw-r--r-- | adservices/tests/unittest/service-core/src/com/android/adservices/service/measurement/reporting/DebugReportingJobServiceTest.java | 287 |
1 files changed, 287 insertions, 0 deletions
diff --git a/adservices/tests/unittest/service-core/src/com/android/adservices/service/measurement/reporting/DebugReportingJobServiceTest.java b/adservices/tests/unittest/service-core/src/com/android/adservices/service/measurement/reporting/DebugReportingJobServiceTest.java new file mode 100644 index 0000000000..c18f231b17 --- /dev/null +++ b/adservices/tests/unittest/service-core/src/com/android/adservices/service/measurement/reporting/DebugReportingJobServiceTest.java @@ -0,0 +1,287 @@ +/* + * 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.measurement.reporting; + +import static com.android.adservices.service.AdServicesConfig.MEASUREMENT_DEBUG_REPORT_JOB_ID; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +import android.app.job.JobInfo; +import android.app.job.JobParameters; +import android.app.job.JobScheduler; +import android.content.Context; + +import com.android.adservices.data.enrollment.EnrollmentDao; +import com.android.adservices.data.measurement.DatastoreManager; +import com.android.adservices.data.measurement.DatastoreManagerFactory; +import com.android.adservices.service.AdServicesConfig; +import com.android.adservices.service.Flags; +import com.android.adservices.service.FlagsFactory; +import com.android.compatibility.common.util.TestUtils; +import com.android.dx.mockito.inline.extended.ExtendedMockito; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mockito; +import org.mockito.MockitoSession; +import org.mockito.quality.Strictness; + +import java.util.Optional; + +/** + * Unit test for {@link DebugReportingJobService + */ +public class DebugReportingJobServiceTest { + + private static final long WAIT_IN_MILLIS = 1000L; + + private DatastoreManager mMockDatastoreManager; + private JobScheduler mMockJobScheduler; + + private DebugReportingJobService mSpyService; + + @Before + public void setUp() { + mSpyService = spy(new DebugReportingJobService()); + mMockDatastoreManager = mock(DatastoreManager.class); + mMockJobScheduler = mock(JobScheduler.class); + } + + @Test + public void onStartJob_killSwitchOn() throws Exception { + runWithMocks( + () -> { + // Setup + enableKillSwitch(); + + // Execute + boolean result = mSpyService.onStartJob(Mockito.mock(JobParameters.class)); + + // Validate + assertFalse(result); + // Allow background thread to execute + Thread.sleep(WAIT_IN_MILLIS); + verify(mMockDatastoreManager, never()).runInTransactionWithResult(any()); + verify(mSpyService, times(1)).jobFinished(any(), eq(false)); + verify(mMockJobScheduler, times(1)).cancel(eq(MEASUREMENT_DEBUG_REPORT_JOB_ID)); + }); + } + + @Test + public void onStartJob_killSwitchOff() throws Exception { + runWithMocks( + () -> { + // Setup + disableKillSwitch(); + + ExtendedMockito.doNothing() + .when( + () -> + DebugReportingJobService.scheduleIfNeeded( + any(), anyBoolean())); + + // Execute + boolean result = mSpyService.onStartJob(Mockito.mock(JobParameters.class)); + + // Validate + assertTrue(result); + // Allow background thread to execute + Thread.sleep(WAIT_IN_MILLIS); + verify(mMockDatastoreManager, times(2)).runInTransactionWithResult(any()); + verify(mSpyService, times(1)).jobFinished(any(), anyBoolean()); + verify(mMockJobScheduler, never()).cancel(eq(MEASUREMENT_DEBUG_REPORT_JOB_ID)); + }); + } + + @Test + public void scheduleIfNeeded_killSwitchOn_dontSchedule() throws Exception { + runWithMocks( + () -> { + // Setup + enableKillSwitch(); + + final Context mockContext = mock(Context.class); + doReturn(mMockJobScheduler) + .when(mockContext) + .getSystemService(JobScheduler.class); + final JobInfo mockJobInfo = mock(JobInfo.class); + doReturn(mockJobInfo) + .when(mMockJobScheduler) + .getPendingJob(eq(MEASUREMENT_DEBUG_REPORT_JOB_ID)); + + // Execute + DebugReportingJobService.scheduleIfNeeded( + mockContext, /* forceSchedule = */ false); + + // Validate + // Allow background thread to execute + Thread.sleep(WAIT_IN_MILLIS); + ExtendedMockito.verify( + () -> DebugReportingJobService.schedule(any(), any()), never()); + verify(mMockJobScheduler, never()) + .getPendingJob(eq(MEASUREMENT_DEBUG_REPORT_JOB_ID)); + }); + } + + @Test + public void scheduleIfNeeded_killSwitchOff_previouslyExecuted_dontForceSchedule_dontSchedule() + throws Exception { + runWithMocks( + () -> { + // Setup + disableKillSwitch(); + + final Context mockContext = mock(Context.class); + doReturn(mMockJobScheduler) + .when(mockContext) + .getSystemService(JobScheduler.class); + final JobInfo mockJobInfo = mock(JobInfo.class); + doReturn(mockJobInfo) + .when(mMockJobScheduler) + .getPendingJob(eq(MEASUREMENT_DEBUG_REPORT_JOB_ID)); + + // Execute + DebugReportingJobService.scheduleIfNeeded( + mockContext, /* forceSchedule = */ false); + + // Validate + // Allow background thread to execute + Thread.sleep(WAIT_IN_MILLIS); + ExtendedMockito.verify( + () -> DebugReportingJobService.schedule(any(), any()), never()); + verify(mMockJobScheduler, times(1)) + .getPendingJob(eq(MEASUREMENT_DEBUG_REPORT_JOB_ID)); + }); + } + + @Test + public void scheduleIfNeeded_killSwitchOff_previouslyExecuted_forceSchedule_schedule() + throws Exception { + runWithMocks( + () -> { + // Setup + disableKillSwitch(); + + final Context mockContext = mock(Context.class); + doReturn(mMockJobScheduler) + .when(mockContext) + .getSystemService(JobScheduler.class); + final JobInfo mockJobInfo = mock(JobInfo.class); + doReturn(mockJobInfo) + .when(mMockJobScheduler) + .getPendingJob(eq(MEASUREMENT_DEBUG_REPORT_JOB_ID)); + + // Execute + DebugReportingJobService.scheduleIfNeeded( + mockContext, /* forceSchedule = */ true); + + // Validate + // Allow background thread to execute + Thread.sleep(WAIT_IN_MILLIS); + ExtendedMockito.verify( + () -> DebugReportingJobService.schedule(any(), any()), times(1)); + verify(mMockJobScheduler, times(1)) + .getPendingJob(eq(MEASUREMENT_DEBUG_REPORT_JOB_ID)); + }); + } + + @Test + public void scheduleIfNeeded_killSwitchOff_previouslyNotExecuted_dontForceSchedule_schedule() + throws Exception { + runWithMocks( + () -> { + // Setup + disableKillSwitch(); + + final Context mockContext = mock(Context.class); + doReturn(mMockJobScheduler) + .when(mockContext) + .getSystemService(JobScheduler.class); + doReturn(/* noJobInfo = */ null) + .when(mMockJobScheduler) + .getPendingJob(eq(MEASUREMENT_DEBUG_REPORT_JOB_ID)); + + // Execute + DebugReportingJobService.scheduleIfNeeded(mockContext, false); + + // Validate + // Allow background thread to execute + Thread.sleep(WAIT_IN_MILLIS); + ExtendedMockito.verify( + () -> DebugReportingJobService.schedule(any(), any()), times(1)); + verify(mMockJobScheduler, times(1)) + .getPendingJob(eq(MEASUREMENT_DEBUG_REPORT_JOB_ID)); + }); + } + + private void runWithMocks(TestUtils.RunnableWithThrow execute) throws Exception { + MockitoSession session = + ExtendedMockito.mockitoSession() + .spyStatic(AdServicesConfig.class) + .spyStatic(DatastoreManagerFactory.class) + .spyStatic(EnrollmentDao.class) + .spyStatic(DebugReportingJobService.class) + .spyStatic(FlagsFactory.class) + .strictness(Strictness.LENIENT) + .startMocking(); + try { + // Setup mock everything in job + mMockDatastoreManager = mock(DatastoreManager.class); + doReturn(Optional.empty()) + .when(mMockDatastoreManager) + .runInTransactionWithResult(any()); + doNothing().when(mSpyService).jobFinished(any(), anyBoolean()); + doReturn(mMockJobScheduler).when(mSpyService).getSystemService(JobScheduler.class); + doReturn(Mockito.mock(Context.class)).when(mSpyService).getApplicationContext(); + ExtendedMockito.doReturn(mock(EnrollmentDao.class)) + .when(() -> EnrollmentDao.getInstance(any())); + ExtendedMockito.doReturn(mMockDatastoreManager) + .when(() -> DatastoreManagerFactory.getDatastoreManager(any())); + ExtendedMockito.doNothing().when(() -> DebugReportingJobService.schedule(any(), any())); + + // Execute + execute.run(); + } finally { + session.finishMocking(); + } + } + + private void enableKillSwitch() { + toggleKillSwitch(true); + } + + private void disableKillSwitch() { + toggleKillSwitch(false); + } + + private void toggleKillSwitch(boolean value) { + Flags mockFlags = Mockito.mock(Flags.class); + ExtendedMockito.doReturn(mockFlags).when(FlagsFactory::getFlags); + ExtendedMockito.doReturn(value).when(mockFlags).getMeasurementJobDebugReportingKillSwitch(); + } +} |