Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ import androidx.work.WorkManager
import com.duckduckgo.app.CoroutineTestRule
import com.duckduckgo.app.notification.NotificationScheduler.*
import com.duckduckgo.app.notification.model.SchedulableNotification
import com.duckduckgo.app.notification.model.SearchNotification
import com.duckduckgo.app.statistics.VariantManager
import com.duckduckgo.app.statistics.VariantManager.Companion.DEFAULT_VARIANT
import com.nhaarman.mockitokotlin2.any
Expand All @@ -48,7 +47,6 @@ class AndroidNotificationSchedulerTest {
private val variantManager: VariantManager = mock()
private val clearNotification: SchedulableNotification = mock()
private val privacyNotification: SchedulableNotification = mock()
private val searchPromptNotification: SearchNotification = mock()

private val context = InstrumentationRegistry.getInstrumentation().targetContext
private var workManager = WorkManager.getInstance(context)
Expand All @@ -60,136 +58,59 @@ class AndroidNotificationSchedulerTest {
testee = NotificationScheduler(
workManager,
clearNotification,
privacyNotification,
searchPromptNotification
privacyNotification
)
}

@After
fun resetWorkers() {
workManager.cancelAllWorkByTag(NotificationScheduler.CONTINUOUS_APP_USE_REQUEST_TAG)
}

@Test
fun whenPrivacyNotificationClearDataAndSearchPromptCanShowThenBothAreScheduled() = runBlocking<Unit> {
whenever(privacyNotification.canShow()).thenReturn(true)
whenever(clearNotification.canShow()).thenReturn(true)
whenever(searchPromptNotification.canShow()).thenReturn(true)
testee.scheduleNextNotification()

assertUnusedAppNotificationScheduled(PrivacyNotificationWorker::class.jvmName)
assertContinuousAppUseNotificationScheduled(SearchPromptNotificationWorker::class.jvmName)
}

@Test
fun whenPrivacyNotificationClearDataAndSearchPromptCanShowThenPrivacyNotificationScheduled() = runBlocking<Unit> {
fun whenPrivacyNotificationClearDataCanShowThenPrivacyNotificationIsScheduled() = runBlocking<Unit> {
whenever(privacyNotification.canShow()).thenReturn(true)
whenever(clearNotification.canShow()).thenReturn(true)
whenever(searchPromptNotification.canShow()).thenReturn(false)
testee.scheduleNextNotification()

assertUnusedAppNotificationScheduled(PrivacyNotificationWorker::class.jvmName)
assertNoContinuousAppNotificationScheduled()
}

@Test
fun whenPrivacyNotificationAndSearchPromptCanShowButClearDataCannotThenThenBothAreScheduled() = runBlocking<Unit> {
fun whenPrivacyNotificationCanShowButClearDataCannotThenPrivacyNotificationIsScheduled() = runBlocking<Unit> {
whenever(privacyNotification.canShow()).thenReturn(true)
whenever(clearNotification.canShow()).thenReturn(false)
whenever(searchPromptNotification.canShow()).thenReturn(true)
testee.scheduleNextNotification()

assertUnusedAppNotificationScheduled(PrivacyNotificationWorker::class.jvmName)
assertContinuousAppUseNotificationScheduled(SearchPromptNotificationWorker::class.jvmName)
}

@Test
fun whenPrivacyNotificationCanShowButClearDataAndSearchPromptCannotThenPrivacyNotificationScheduled() = runBlocking<Unit> {
whenever(privacyNotification.canShow()).thenReturn(true)
whenever(clearNotification.canShow()).thenReturn(false)
whenever(searchPromptNotification.canShow()).thenReturn(false)
testee.scheduleNextNotification()

assertUnusedAppNotificationScheduled(PrivacyNotificationWorker::class.jvmName)
assertNoContinuousAppNotificationScheduled()
}

@Test
fun whenPrivacyNotificationAndSearchPromptCannotShowAndClearNotificationCanShowThenBothAreScheduled() = runBlocking<Unit> {
fun whenPrivacyNotificationCannotShowAndClearNotificationCanShowThenClearNotificationIsScheduled() = runBlocking<Unit> {
whenever(privacyNotification.canShow()).thenReturn(false)
whenever(clearNotification.canShow()).thenReturn(true)
whenever(searchPromptNotification.canShow()).thenReturn(true)
testee.scheduleNextNotification()

assertUnusedAppNotificationScheduled(ClearDataNotificationWorker::class.jvmName)
assertContinuousAppUseNotificationScheduled(SearchPromptNotificationWorker::class.jvmName)
}

@Test
fun whenPrivacyNotificationAndClearNotificationCannotShowButSearchPromptCanShowThenNotificationScheduled() = runBlocking<Unit> {
fun whenPrivacyNotificationAndClearNotificationCannotShowThenNoNotificationScheduled() = runBlocking<Unit> {
whenever(privacyNotification.canShow()).thenReturn(false)
whenever(clearNotification.canShow()).thenReturn(false)
whenever(searchPromptNotification.canShow()).thenReturn(true)
testee.scheduleNextNotification()

assertContinuousAppUseNotificationScheduled(SearchPromptNotificationWorker::class.jvmName)
assertNoUnusedAppNotificationScheduled()
}

@Test
fun whenPrivacyNotificationAndClearNotificationCannotShowButSearchPromptCanThenSearchPromptNotificationScheduled() = runBlocking<Unit> {
whenever(privacyNotification.canShow()).thenReturn(false)
whenever(clearNotification.canShow()).thenReturn(false)
whenever(searchPromptNotification.canShow()).thenReturn(true)

testee.scheduleNextNotification()

assertContinuousAppUseNotificationScheduled(SearchPromptNotificationWorker::class.jvmName)
assertNoUnusedAppNotificationScheduled()
}

@Test
fun whenNoNotificationCanShowThenNoNotificationScheduled() = runBlocking<Unit> {
whenever(privacyNotification.canShow()).thenReturn(false)
whenever(clearNotification.canShow()).thenReturn(false)
whenever(searchPromptNotification.canShow()).thenReturn(false)
testee.scheduleNextNotification()

assertNoNotificationScheduled()
}

private fun assertUnusedAppNotificationScheduled(workerName: String) {
assertTrue(getUnusedAppScheduledWorkers().any { it.tags.contains(workerName) })
}

private fun assertContinuousAppUseNotificationScheduled(workerName: String) {
assertTrue(getContinuousAppUseScheduledWorkers().any { it.tags.contains(workerName) })
}

private fun assertNoUnusedAppNotificationScheduled() {
assertTrue(getUnusedAppScheduledWorkers().isEmpty())
}

private fun assertNoContinuousAppNotificationScheduled() {
assertTrue(getContinuousAppUseScheduledWorkers().isEmpty())
}

private fun assertNoNotificationScheduled() {
assertTrue(getUnusedAppScheduledWorkers().isEmpty())
assertTrue(getContinuousAppUseScheduledWorkers().isEmpty())
}

private fun getUnusedAppScheduledWorkers(): List<WorkInfo> {
return workManager
.getWorkInfosByTag(NotificationScheduler.UNUSED_APP_WORK_REQUEST_TAG)
.get()
.filter { it.state == WorkInfo.State.ENQUEUED }
}

private fun getContinuousAppUseScheduledWorkers(): List<WorkInfo> {
return workManager
.getWorkInfosByTag(NotificationScheduler.CONTINUOUS_APP_USE_REQUEST_TAG)
.get()
.filter { it.state == WorkInfo.State.ENQUEUED }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -67,9 +67,6 @@ class SettingsViewModelTest {
@Mock
private lateinit var mockVariantManager: VariantManager

@Mock
private lateinit var notificationScheduler: AndroidNotificationScheduler

@Mock
private lateinit var mockPixel: Pixel

Expand All @@ -82,7 +79,7 @@ class SettingsViewModelTest {
context = InstrumentationRegistry.getInstrumentation().targetContext
commandCaptor = argumentCaptor()

testee = SettingsViewModel(mockAppSettingsDataStore, mockDefaultBrowserDetector, mockVariantManager, mockPixel, notificationScheduler)
testee = SettingsViewModel(mockAppSettingsDataStore, mockDefaultBrowserDetector, mockVariantManager, mockPixel)
testee.command.observeForever(commandObserver)

whenever(mockAppSettingsDataStore.automaticallyClearWhenOption).thenReturn(APP_EXIT_ONLY)
Expand Down Expand Up @@ -230,32 +227,5 @@ class SettingsViewModelTest {
assertEquals(Command.LaunchAppIcon, commandCaptor.firstValue)
}

@Test
fun whenSearchNotificationWasPreviouslyEnabledThenViewStateIndicatesIt() {
whenever(mockAppSettingsDataStore.searchNotificationEnabled).thenReturn(true)
testee.start()
assertTrue(latestViewState().searchNotificationEnabled)
}

@Test
fun whenSearchNotificationToggledOnThenDataStoreIsUpdatedAndNotificationShown() {
testee.onSearchNotificationSettingChanged(true)
verify(mockAppSettingsDataStore).searchNotificationEnabled = true
verify(notificationScheduler).launchStickySearchNotification()
verify(mockPixel).fire(Pixel.PixelName.QUICK_SEARCH_NOTIFICATION_ENABLED)

assertTrue(latestViewState().searchNotificationEnabled)
}

@Test
fun whenSearchNotificationToggledOffThenDataStoreIsUpdatedAndNotificationRemoved() {
testee.onSearchNotificationSettingChanged(false)
verify(mockAppSettingsDataStore).searchNotificationEnabled = false
verify(notificationScheduler).dismissStickySearchNotification()
verify(mockPixel).fire(Pixel.PixelName.QUICK_SEARCH_NOTIFICATION_DISABLED)

assertFalse(latestViewState().searchNotificationEnabled)
}

private fun latestViewState() = testee.viewState.value!!
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,23 +43,6 @@ class VariantManagerTest {
assertEquals(0, variant.features.size)
}

// Search Notification Experiment

@Test
fun searchNotificationControlVariantIsActiveAndHasNoFeatures() {
val variant = variants.first { it.key == "mf" }
assertEqualsDouble(1.0, variant.weight)
assertEquals(0, variant.features.size)
}

@Test
fun searchNotificationVariantIsActiveAndHasStickySearchNotificationFeature() {
val variant = variants.first { it.key == "mg" }
assertEqualsDouble(1.0, variant.weight)
assertEquals(1, variant.features.size)
assertTrue(variant.hasFeature(StickySearchNotification))
}

@Test
fun verifyNoDuplicateVariantNames() {
val existingNames = mutableSetOf<String>()
Expand Down
31 changes: 0 additions & 31 deletions app/src/main/java/com/duckduckgo/app/di/DaggerWorkerFactory.kt
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,10 @@ import com.duckduckgo.app.fire.DataClearingWorker
import com.duckduckgo.app.global.view.ClearDataAction
import com.duckduckgo.app.notification.NotificationScheduler.ClearDataNotificationWorker
import com.duckduckgo.app.notification.NotificationScheduler.PrivacyNotificationWorker
import com.duckduckgo.app.notification.NotificationScheduler.SearchPromptNotificationWorker
import com.duckduckgo.app.notification.NotificationScheduler.StickySearchNotificationWorker
import com.duckduckgo.app.notification.NotificationScheduler.DismissSearchNotificationWorker
import com.duckduckgo.app.notification.NotificationFactory
import com.duckduckgo.app.notification.db.NotificationDao
import com.duckduckgo.app.notification.model.ClearDataNotification
import com.duckduckgo.app.notification.model.PrivacyProtectionNotification
import com.duckduckgo.app.notification.model.SearchPromptNotification
import com.duckduckgo.app.notification.model.StickySearchNotification
import com.duckduckgo.app.settings.db.SettingsDataStore
import com.duckduckgo.app.statistics.api.OfflinePixelScheduler
import com.duckduckgo.app.statistics.api.OfflinePixelSender
Expand All @@ -49,8 +44,6 @@ class DaggerWorkerFactory(
private val notificationFactory: NotificationFactory,
private val clearDataNotification: ClearDataNotification,
private val privacyProtectionNotification: PrivacyProtectionNotification,
private val stickySearchPromptNotification: SearchPromptNotification,
private val stickySearchNotification: StickySearchNotification,
private val pixel: Pixel
) : WorkerFactory() {

Expand All @@ -66,9 +59,6 @@ class DaggerWorkerFactory(
is DataClearingWorker -> injectDataClearWorker(instance)
is ClearDataNotificationWorker -> injectClearDataNotificationWorker(instance)
is PrivacyNotificationWorker -> injectPrivacyNotificationWorker(instance)
is SearchPromptNotificationWorker -> injectSearchPromptNotificationWorker(instance)
is StickySearchNotificationWorker -> injectStickySearchNotificationWorker(instance)
is DismissSearchNotificationWorker -> injectDismissSearchNotificationWorker(instance)
else -> Timber.i("No injection required for worker $workerClassName")
}

Expand Down Expand Up @@ -105,25 +95,4 @@ class DaggerWorkerFactory(
worker.notification = privacyProtectionNotification
}

private fun injectSearchPromptNotificationWorker(worker: SearchPromptNotificationWorker) {
worker.manager = notificationManager
worker.notificationDao = notificationDao
worker.factory = notificationFactory
worker.pixel = pixel
worker.notification = stickySearchPromptNotification
}

private fun injectStickySearchNotificationWorker(worker: StickySearchNotificationWorker) {
worker.manager = notificationManager
worker.notificationDao = notificationDao
worker.factory = notificationFactory
worker.pixel = pixel
worker.notification = stickySearchNotification
}

private fun injectDismissSearchNotificationWorker(worker: DismissSearchNotificationWorker) {
worker.manager = notificationManager
worker.notificationDao = notificationDao
worker.notification = stickySearchNotification
}
}
27 changes: 2 additions & 25 deletions app/src/main/java/com/duckduckgo/app/di/NotificationModule.kt
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,8 @@ import com.duckduckgo.app.notification.AndroidNotificationScheduler
import com.duckduckgo.app.notification.db.NotificationDao
import com.duckduckgo.app.notification.model.ClearDataNotification
import com.duckduckgo.app.notification.model.PrivacyProtectionNotification
import com.duckduckgo.app.notification.model.SearchPromptNotification
import com.duckduckgo.app.notification.model.StickySearchNotification
import com.duckduckgo.app.privacy.db.PrivacyProtectionCountDao
import com.duckduckgo.app.settings.db.SettingsDataStore
import com.duckduckgo.app.statistics.VariantManager
import dagger.Module
import dagger.Provides
import javax.inject.Singleton
Expand Down Expand Up @@ -75,37 +72,17 @@ class NotificationModule {
return PrivacyProtectionNotification(context, notificationDao, privacyProtectionCountDao)
}

@Provides
fun provideStickySearchNotification(
context: Context,
notificationDao: NotificationDao
): StickySearchNotification {
return StickySearchNotification(context, notificationDao)
}

@Provides
fun provideSearchPromptNotification(
context: Context,
notificationDao: NotificationDao,
variantManager: VariantManager,
settingsDataStore: SettingsDataStore
): SearchPromptNotification {
return SearchPromptNotification(context, notificationDao, variantManager, settingsDataStore)
}

@Provides
@Singleton
fun providesNotificationScheduler(
workManager: WorkManager,
clearDataNotification: ClearDataNotification,
privacyProtectionNotification: PrivacyProtectionNotification,
stickySearchNotification: StickySearchNotification
privacyProtectionNotification: PrivacyProtectionNotification
): AndroidNotificationScheduler {
return NotificationScheduler(
workManager,
clearDataNotification,
privacyProtectionNotification,
stickySearchNotification
privacyProtectionNotification
)
}

Expand Down
6 changes: 0 additions & 6 deletions app/src/main/java/com/duckduckgo/app/di/WorkerModule.kt
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,6 @@ import com.duckduckgo.app.notification.NotificationFactory
import com.duckduckgo.app.notification.db.NotificationDao
import com.duckduckgo.app.notification.model.ClearDataNotification
import com.duckduckgo.app.notification.model.PrivacyProtectionNotification
import com.duckduckgo.app.notification.model.SearchPromptNotification
import com.duckduckgo.app.notification.model.StickySearchNotification
import com.duckduckgo.app.settings.db.SettingsDataStore
import com.duckduckgo.app.statistics.api.OfflinePixelSender
import com.duckduckgo.app.statistics.pixels.Pixel
Expand Down Expand Up @@ -59,8 +57,6 @@ class WorkerModule {
notificationFactory: NotificationFactory,
clearDataNotification: ClearDataNotification,
privacyProtectionNotification: PrivacyProtectionNotification,
stickySearchNotification: StickySearchNotification,
stickySearchPromptNotification: SearchPromptNotification,
pixel: Pixel
): WorkerFactory {
return DaggerWorkerFactory(
Expand All @@ -72,8 +68,6 @@ class WorkerModule {
notificationFactory,
clearDataNotification,
privacyProtectionNotification,
stickySearchPromptNotification,
stickySearchNotification,
pixel
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -157,8 +157,7 @@ class ViewModelFactory @Inject constructor(
appSettingsPreferencesStore,
defaultBrowserDetector,
variantManager,
pixel,
notificationScheduler
pixel
)
}

Expand Down
Loading