8000 Improve favicon rate (#940) · githublucas420/Android@cab4d81 · GitHub
[go: up one dir, main page]

Skip to content {"props":{"docsUrl":"https://docs.github.com/get-started/accessibility/keyboard-shortcuts"}}

Commit cab4d81

Browse files
Improve favicon rate (duckduckgo#940)
1 parent 4da06c8 commit cab4d81

File tree

50 files changed

+1506
-173
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

50 files changed

+1506
-173
lines changed

app/src/androidTest/java/com/duckduckgo/app/bookmarks/db/BookmarksDaoTest.kt

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,4 +92,20 @@ class BookmarksDaoTest {
9292
assertFalse(dao.hasBookmarks())
9393
}
9494

95+
@Test
96+
fun whenBookmarksCountByUrlAndNoBookmarksMatchThenReturnZero() {
97+
val bookmark = BookmarkEntity(id = 1, title = "title", url = "www.example.com")
98+
dao.insert(bookmark)
99+
val count = dao.bookmarksCountByUrl("test")
100+
assertEquals(0, count)
101+
}
102+
103+
@Test
104+
fun whenBookmarksCountByUrlAndBookmarksMatchThenReturnCount() {
105+
val query = "%example%"
106+
val bookmark = BookmarkEntity(id = 1, title = "title", url = "www.example.com")
107+
dao.insert(bookmark)
108+
val count = dao.bookmarksCountByUrl(query)
109+
assertEquals(1, count)
110+
}
95111
}

app/src/androidTest/java/com/duckduckgo/app/bookmarks/ui/BookmarksViewModelTest.kt

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,16 @@ package com.duckduckgo.app.bookmarks.ui
1919
import androidx.arch.core.executor.testing.InstantTaskExecutorRule
2020
import androidx.lifecycle.MutableLiveData
2121
import androidx.lifecycle.Observer
22+
import com.duckduckgo.app.CoroutineTestRule
2223
import com.duckduckgo.app.InstantSchedulersRule
2324
import com.duckduckgo.app.bookmarks.db.BookmarkEntity
2425
import com.duckduckgo.app.bookmarks.db.BookmarksDao
26+
import com.duckduckgo.app.browser.favicon.FaviconManager
27+
import com.duckduckgo.app.runBlocking
2528
import com.nhaarman.mockitokotlin2.mock
2629
import com.nhaarman.mockitokotlin2.verify
2730
import com.nhaarman.mockitokotlin2.whenever
31+
import kotlinx.coroutines.ExperimentalCoroutinesApi
2832
import org.junit.After
2933
import org.junit.Assert.assertNotNull
3034
import org.junit.Assert.assertTrue
@@ -33,6 +37,7 @@ import org.junit.Rule
3337
import org.junit.Test
3438
import org.mockito.ArgumentCaptor
3539

40+
@ExperimentalCoroutinesApi
3641
class BookmarksViewModelTest {
3742

3843
@get:Rule
@@ -43,15 +48,20 @@ class BookmarksViewModelTest {
4348
@Suppress("unused")
4449
val schedulers = InstantSchedulersRule()
4550

51+
@get:Rule
52+
@Suppress("unused")
53+
val coroutineRule = CoroutineTestRule()
54+
4655
private val liveData = MutableLiveData<List<BookmarkEntity>>()
4756
private val viewStateObserver: Observer<BookmarksViewModel.ViewState> = mock()
4857
private val commandObserver: Observer<BookmarksViewModel.Command> = mock()
4958
private val bookmarksDao: BookmarksDao = mock()
59+
private val faviconManager: FaviconManager = mock()
5060

5161
private val bookmark = BookmarkEntity(title = "title", url = "www.example.com")
5262

5363
private val testee: BookmarksViewModel by lazy {
54-
val model = BookmarksViewModel(bookmarksDao)
64+
val model = BookmarksViewModel(bookmarksDao, faviconManager, coroutineRule.testDispatcherProvider)
5565
model.viewState.observeForever(viewStateObserver)
5666
model.command.observeForever(commandObserver)
5767
model
@@ -101,4 +111,10 @@ class BookmarksViewModelTest {
101111
assertNotNull(captor.value)
102112
assertNotNull(captor.value.bookmarks)
103113
}
114+
115+
@Test
116+
fun whenBookmarkDeletedThenFaviconDeleted() = coroutineRule.runBlocking {
117+
testee.delete(bookmark)
118+
verify(faviconManager).deletePersistedFavicon(bookmark.url)
119+
}
104120
}

app/src/androidTest/java/com/duckduckgo/app/browser/BrowserChromeClientTest.kt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
package com.duckduckgo.app.browser
2020

2121
import android.content.Context
22+
import android.graphics.Bitmap
2223
import android.os.Message
2324
import android.view.View
2425
import android.webkit.WebChromeClient
@@ -112,6 +113,13 @@ class BrowserChromeClientTest {
112113
verifyZeroInteractions(mockWebViewClientListener)
113114
}
114115

116+
@Test
117+
fun whenOnReceivedIconThenIconReceived() {
118+
val bitmap: Bitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.RGB_565)
119+
testee.onReceivedIcon(webView, bitmap)
120+
verify(mockWebViewClientListener).iconReceived(bitmap)
121+
}
122+
115123
private val mockMsg = Message().apply {
116124
target = mock()
117125
obj = mock<WebView.WebViewTransport>()

app/src/androidTest/java/com/duckduckgo/app/browser/BrowserTabViewModelTest.kt

Lines changed: 172 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
package com.duckduckgo.app.browser
1818

19+
import android.graphics.Bitmap
1920
import android.net.Uri
2021
import android.view.MenuItem
2122
import android.view.View
@@ -44,7 +45,7 @@ import com.duckduckgo.app.browser.LongPressHandler.RequiredAction.DownloadFile
4445
import com.duckduckgo.app.browser.LongPressHandler.RequiredAction.OpenInNewTab
4546
import com.duckduckgo.app.browser.addtohome.AddToHomeCapabilityDetector
4647
import com.duckduckgo.app.browser.downloader.FileDownloader
47-
import com.duckduckgo.app.browser.favicon.FaviconDownloader
48+
import com.duckduckgo.app.browser.favicon.FaviconManager
4849
import com.duckduckgo.app.browser.logindetection.LoginDetected
4950
import com.duckduckgo.app.browser.logindetection.NavigationAwareLoginDetector
5051
import com.duckduckgo.app.browser.logindetection.NavigationEvent.LoginAttempt
@@ -103,14 +104,14 @@ import com.duckduckgo.app.trackerdetection.model.TrackingEvent
103104
import com.duckduckgo.app.usage.search.SearchCountDao
104105
import com.duckduckgo.app.widget.ui.WidgetCapabilities
105106
import com.nhaarman.mockitokotlin2.*
107+
import dagger.Lazy
106108
import io.reactivex.Observable
107109
import io.reactivex.Single
108110
import kotlinx.coroutines.ExperimentalCoroutinesApi
109111
import kotlinx.coroutines.flow.emptyFlow
110112
import kotlinx.coroutines.runBlocking
111113
import kotlinx.coroutines.test.runBlockingTest
112114
import org.junit.After
113-
import org.junit.Assert
114115
import org.junit.Assert.*
115116
import org.junit.Before
116117
import org.junit.Rule
@@ -119,6 +120,7 @@ import org.mockito.*
119120
import org.mockito.ArgumentMatchers.anyString
120121
import org.mockito.Mockito.never
121122
import org.mockito.Mockito.verify
123+
import java.io.File
122124
import java.util.concurrent.TimeUnit
123125

124126
@ExperimentalCoroutinesApi
@@ -168,7 +170,7 @@ class BrowserTabViewModelTest {
168170
private lateinit var webViewSessionStorage: WebViewSessionStorage
169171

170172
@Mock
171-
private lateinit var mockFaviconDownloader: FaviconDownloader
173+
private lateinit var mockFaviconManager: FaviconManager
172174

173175
@Mock
174176
private lateinit var mockAddToHomeCapabilityDetector: AddToHomeCapabilityDetector
@@ -221,6 +223,8 @@ class BrowserTabViewModelTest {
221223
@Mock
222224
private lateinit var geoLocationPermissions: GeoLocationPermissions
223225

226+
private val lazyFaviconManager = Lazy { mockFaviconManager }
227+
224228
private lateinit var mockAutoCompleteApi: AutoCompleteApi
225229

226230
private lateinit var ctaViewModel: CtaViewModel
@@ -295,14 +299,18 @@ class BrowserTabViewModelTest {
295299
longPressHandler = mockLongPressHandler,
296300
webViewSessionStorage = webViewSessionStorage,
297301
specialUrlDetector = SpecialUrlDetectorImpl(),
298-
faviconDownloader = mockFaviconDownloader,
302+
faviconManager = mockFaviconManager,
299303
addToHomeCapabilityDetector = mockAddToHomeCapabilityDetector,
300304
ctaViewModel = ctaViewModel,
301305
searchCountDao = mockSearchCountDao,
302306
pixel = mockPixel,
303307
dispatchers = coroutineRule.testDispatcherProvider,
304-
fireproofWebsiteRepository = FireproofWebsiteRepository(fireproofWebsiteDao, coroutineRule.testDispatcherProvider),
305-
locationPermissionsRepository = LocationPermissionsRepository(locationPermissionsDao, coroutineRule.testDispatcherProvider),
308+
fireproofWebsiteRepository = FireproofWebsiteRepository(fireproofWebsiteDao, coroutineRule.testDispatcherProvider, lazyFaviconManager),
309+
locationPermissionsRepository = LocationPermissionsRepository(
310+
locationPermissionsDao,
311+
lazyFaviconManager,
312+
coroutineRule.testDispatcherProvider
313+
),
306314
geoLocationPermissions = geoLocationPermissions,
307315
navigationAwareLoginDetector = mockNavigationAwareLoginDetector,
308316
userEventsStore = mockUserEventsStore,
@@ -2541,7 +2549,7 @@ class BrowserTabViewModelTest {
25412549

25422550
verify(mockPixel).fire(Pixel.PixelName.PRECISE_LOCATION_SYSTEM_DIALOG_NEVER)
25432551
verify(geoLocationPermissions).clear(domain)
2544-
Assert.assertEquals(locationPermissionsDao.getPermission(domain)!!.permission, LocationPermissionType.DENY_ALWAYS)
2552+
assertEquals(locationPermissionsDao.getPermission(domain)!!.permission, LocationPermissionType.DENY_ALWAYS)
25452553
}
25462554

25472555
@Test
@@ -2673,6 +2681,163 @@ class BrowserTabViewModelTest {
26732681
verify(mockSettingsStore).appLocationPermissionDeniedForever = false
26742682
}
26752683

2684+
@Test
2685+
fun whenPrefetchFaviconThenPrefetchToTemp() = coroutineRule.runBlocking {
2686+
val url = "https://www.example.com/"
2687+
givenCurrentSite(url)
2688+
testee.prefetchFavicon(url)
2689+
2690+
verify(mockFaviconManager).prefetchToTemp("TAB_ID", url)
2691+
}
2692+
2693+
@Test
2694+
fun whenPrefetchFaviconAndFaviconExistsThenUpdateTabFavicon() = coroutineRule.runBlocking {
2695+
val url = "https://www.example.com/"
2696+
val file = File("test")
2697+
givenCurrentSite(url)
2698+
whenever(mockFaviconManager.prefetchToTemp(any(), any())).thenReturn(file)
2699+
2700+
testee.prefetchFavicon(url)
2701+
2702+
verify(mockTabsRepository).updateTabFavicon("TAB_ID", file.name)
2703+
}
2704+
2705+
@Test
2706+
fun whenPrefetchFaviconAndFaviconDoesNotExistThenDoNotCallUpdateTabFavicon() = coroutineRule.runBlocking {
2707+
whenever(mockFaviconManager.prefetchToTemp(any(), any())).thenReturn(null)
2708+
2709+
testee.prefetchFavicon("url")
2710+
2711+
verify(mockTabsRepository, never()).updateTabFavicon(any(), any())
2712+
}
2713+
2714+
@Test
2715+
fun whenIconReceivedThenSaveToTemp() = coroutineRule.runBlocking {
2716+
givenOneActiveTabSelected()
2717+
val bitmap: Bitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.RGB_565)
2718+
2719+
testee.iconReceived(bitmap)
2720+
2721+
verify(mockFaviconManager).saveToTemp("TAB_ID", bitmap, "https://example.com")
2722+
}
2723+
2724+
@Test
2725+
fun whenIconReceivedIfCorrectlySavedThenUpdateTabFavicon() = coroutineRule.runBlocking {
2726+
givenOneActiveTabSelected()
2727+
val bitmap: Bitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.RGB_565)
2728+
val file = File("test")
2729+
whenever(mockFaviconManager.saveToTemp(any(), any(), any())).thenReturn(file)
2730+
2731+
testee.iconReceived(bitmap)
2732+
2733+
verify(mockTabsRepository).updateTabFavicon("TAB_ID", file.name)
2734+
}
2735+
2736+
@Test
2737+
fun whenIconReceivedIfNotCorrectlySavedThenDoNotUpdateTabFavicon() = coroutineRule.runBlocking {
2738+
givenOneActiveTabSelected()
2739+
val bitmap: Bitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.RGB_565)
2740+
whenever(mockFaviconManager.saveToTemp(any(), any(), any())).thenReturn(null)
2741+
2742+
testee.iconReceived(bitmap)
2743+
2744+
verify(mockTabsRepository, never()).updateTabFavicon(any(), any())
2745+
}
2746+
2747+
@Test
2748+
fun whenOnSiteLocationPermissionSelectedAndPermissionIsAllowAlwaysThenPersistFavicon() = coroutineRule.runBlocking {
2749+
val url = "http://example.com"
2750+
val permission = LocationPermissionType.ALLOW_ALWAYS
2751+
givenNewPermissionRequestFromDomain(url)
2752+
2753+
testee.onSiteLocationPermissionSelected(url, permission)
2754+
2755+
verify(mockFaviconManager).persistFavicon(any(), eq(url))
2756+
}
2757+
2758+
@Test
2759+
fun whenOnSiteLocationPermissionSelectedAndPermissionIsDenyAlwaysThenPersistFavicon() = coroutineRule.runBlocking {
2760+
val url = "http://example.com"
2761+
val permission = LocationPermissionType.DENY_ALWAYS
2762+
givenNewPermissionRequestFromDomain(url)
2763+
2764+
testee.onSiteLocationPermissionSelected(url, permission)
2765+
2766+
verify(mockFaviconManager).persistFavicon(any(), eq(url))
2767+
}
2768+
2769+
@Test
2770+
fun whenOnSystemLocationPermissionNeverAllowedThenPersistFavicon() = coroutineRule.runBlocking {
2771+
val url = "http://example.com"
2772+
givenNewPermissionRequestFromDomain(url)
2773+
2774+
testee.onSystemLocationPermissionNeverAllowed()
2775+
2776+
verify(mockFaviconManager).persistFavicon(any(), eq(url))
2777+
}
2778+
2779+
@Test
2780+
fun whenBookmarkAddedThenPersistFavicon() = coroutineRule.runBlocking {
2781+
val url = "http://example.com"
2782+
loadUrl(url, "A title")
2783+
2784+
testee.onBookmarkAddRequested()
2785+
2786+
verify(mockFaviconManager).persistFavicon(any(), eq(url))
2787+
}
2788+
2789+
@Test
2790+
fun whenBookmarkAddedButUrlIsNullThenDoNotPersistFavicon() = coroutineRule.runBlocking {
2791+
loadUrl(null, "A title")
2792+
2793+
testee.onBookmarkAddRequested()
2794+
2795+
verify(mockFaviconManager, never()).persistFavicon(any(), any())
2796+
}
2797+
2798+
@Test
2799+
fun whenFireproofWebsiteAddedThenPersistFavicon() = coroutineRule.runBlocking {
2800+
val url = "http://example.com"
2801+
loadUrl(url, isBrowserShowing = true)
2802+
2803+
testee.onFireproofWebsiteMenuClicked()
2804+
2805+
assertCommandIssued<Command.ShowFireproofWebSiteConfirmation> {
2806+
verify(mockFaviconManager).persistFavicon(any(), eq(this.fireproofWebsiteEntity.domain))
2807+
}
2808+
}
2809+
2810+
@Test
2811+
fun whenOnPinPageToHomeSelectedThenAddHomeShortcutCommandIssuedWithFavicon() = coroutineRule.runBlocking {
2812+
val url = "http://example.com"
2813+
val bitmap: Bitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.RGB_565)
2814+
whenever(mockFaviconManager.loadFromTemp(any(), any())).thenReturn(bitmap)
2815+
loadUrl(url, "A title")
2816+
2817+
testee.onPinPageToHomeSelected()
2818+
2819+
assertCommandIssued<Command.AddHomeShortcut> {
2820+
assertEquals(bitmap, this.icon)
2821+
assertEquals(url, this.url)
2822+
assertEquals("example.com", this.title)
2823+
}
2824+
}
2825+
2826+
@Test
2827+
fun whenOnPinPageToHomeSelectedAndFaviconDoesNotExistThenAddHomeShortcutCommandIssuedWithoutFavicon() = coroutineRule.runBlocking {
2828+
val url = "http://example.com"
2829+
whenever(mockFaviconManager.loadFromTemp(any(), any())).thenReturn(null)
2830+
loadUrl(url, "A title")
2831+
2832+
testee.onPinPageToHomeSelected()
2833+
2834+
assertCommandIssued<Command.AddHomeShortcut> {
2835+
assertNull(this.icon)
2836+
assertEquals(url, this.url)
2837+
assertEquals("example.com", this.title)
2838+
}
2839+
}
2840+
26762841
private fun givenNewPermissionRequestFromDomain(domain: String) {
26772842
testee.onSiteLocationPermissionRequested(domain, StubPermissionCallback())
26782843
}

app/src/androidTest/java/com/duckduckgo/app/browser/BrowserWebViewClientTest.kt

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,20 @@ class BrowserWebViewClientTest {
152152
verify(listener, times(1)).recoverFromRenderProcessGone()
153153
}
154154

155+
@UiThreadTest
156+
@Test
157+
fun whenOnPageFinishedCalledThenPrefetchIconCalled() {
158+
testee.onPageFinished(webView, EXAMPLE_URL)
159+
verify(listener).prefetchFavicon(EXAMPLE_URL)
160+
}
161+
162+
@UiThreadTest
163+
@Test
164+
fun whenOnPageFinishedCalledIfUrlIsNullThenDoNotCallPrefetchIcon() {
165+
testee.onPageFinished(webView, null)
166+
verify(listener, never()).prefetchFavicon(any())
167+
}
168+
155169
private class TestWebView(context: Context) : WebView(context)
156170

157171
companion object {

0 commit comments

Comments
 (0)
0