Skip to content

Commit 4a40d4e

Browse files
authored
Adds privacy grade to the dashboard
1 parent f139025 commit 4a40d4e

File tree

95 files changed

+526
-70
lines changed

Some content is hidden

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

95 files changed

+526
-70
lines changed

app/build.gradle

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@ android {
2525
release
2626
}
2727
buildTypes {
28+
debug {
29+
testCoverageEnabled = true
30+
}
2831
release {
2932
minifyEnabled false
3033
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'

app/lint.xml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
<?xml version="1.0" encoding="UTF-8"?>
22
<lint>
33
<issue id="IconColors">
4-
<ignore path="src/main/res/drawable*/privacy_icon_unknown.*" />
4+
<ignore path="src/main/res/drawable*/privacygrade_icon_*" />
55
</issue>
66
<issue id="IconExpectedSize">
7-
<ignore path="src/main/res/drawable-*/privacy_icon_unknown.*" />
7+
<ignore path="src/main/res/drawable-*/privacygrade_icon_*" />
88
</issue>
99
</lint>

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import com.duckduckgo.app.browser.BrowserViewModel.ViewState
2424
import com.duckduckgo.app.browser.omnibar.OmnibarEntryConverter
2525
import com.duckduckgo.app.privacymonitor.store.PrivacyMonitorRepository
2626
import com.duckduckgo.app.privacymonitor.store.TermsOfServiceStore
27+
import com.duckduckgo.app.trackerdetection.model.TrackerNetworks
2728
import com.nhaarman.mockito_kotlin.mock
2829
import org.junit.After
2930
import org.junit.Assert.*
@@ -58,7 +59,7 @@ class BrowserViewModelTest {
5859
queryObserver = mock()
5960
navigationObserver = mock()
6061
termsOfServiceStore = mock()
61-
testee = BrowserViewModel(testOmnibarConverter, DuckDuckGoUrlDetector(), termsOfServiceStore, PrivacyMonitorRepository())
62+
testee = BrowserViewModel(testOmnibarConverter, DuckDuckGoUrlDetector(), termsOfServiceStore, TrackerNetworks(), PrivacyMonitorRepository())
6263
testee.query.observeForever(queryObserver)
6364
testee.viewState.observeForever(viewStateObserver)
6465
testee.navigation.observeForever(navigationObserver)

app/src/androidTest/java/com/duckduckgo/app/global/UriExtensionTest.kt

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,4 +90,16 @@ class UriExtensionTest {
9090
fun whenUriIsMalformedThenIsHtpsIsFalse() {
9191
assertFalse(Uri.parse("[example com]").isHttps)
9292
}
93-
}
93+
94+
@Test
95+
fun whenIpUriThenHasIpHostIsTrue() {
96+
assertTrue(Uri.parse("https://54.229.105.203/something").hasIpHost)
97+
assertTrue(Uri.parse("54.229.105.203/something").hasIpHost)
98+
}
99+
100+
@Test
101+
fun whenStandardUriThenHasIpHostIsFalse() {
102+
assertFalse(Uri.parse("http://example.com").hasIpHost)
103+
}
104+
105+
}
Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
/*
2+
* Copyright (c) 2017 DuckDuckGo
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.duckduckgo.app.privacymonitor
18+
19+
import com.duckduckgo.app.privacymonitor.HttpsStatus.*
20+
import com.duckduckgo.app.privacymonitor.model.TermsOfService
21+
import com.duckduckgo.app.privacymonitor.ui.improvedScore
22+
import com.duckduckgo.app.privacymonitor.ui.score
23+
import com.duckduckgo.app.trackerdetection.model.TrackerNetwork
24+
import com.nhaarman.mockito_kotlin.mock
25+
import com.nhaarman.mockito_kotlin.whenever
26+
import org.junit.Assert.assertEquals
27+
import org.junit.Test
28+
29+
class PrivacyMonitorGradeExtensionTest {
30+
31+
companion object {
32+
val defaultScore = 1
33+
}
34+
35+
@Test
36+
fun whenHttpsMixedThenScoreIsIncrementedByOne() {
37+
val privacyMonitor = monitor(https = MIXED)
38+
assertEquals(defaultScore + 1, privacyMonitor.score)
39+
}
40+
41+
@Test
42+
fun whenHttpThenScoreIsIncrementedByOne() {
43+
val privacyMonitor = monitor(https = NONE)
44+
assertEquals(defaultScore + 1, privacyMonitor.score)
45+
}
46+
47+
@Test
48+
fun whenHttpsThenScoreIsUnchanged() {
49+
val privacyMonitor = monitor(https = SECURE)
50+
assertEquals(defaultScore, privacyMonitor.score)
51+
}
52+
53+
@Test
54+
fun whenTermsClassificationIsAThenScoreIsDecrementedByOne() {
55+
val privacyMonitor = monitor(terms = TermsOfService(classification = "A"))
56+
assertEquals(defaultScore - 1, privacyMonitor.score)
57+
}
58+
59+
@Test
60+
fun whenTermsClassificationIsBThenScoreIsUnchanged() {
61+
val privacyMonitor = monitor(terms = TermsOfService(classification = "B"))
62+
assertEquals(defaultScore, privacyMonitor.score)
63+
}
64+
65+
@Test
66+
fun whenTermsClassificationIsCThenScoreIsUnchanged() {
67+
val privacyMonitor = monitor(terms = TermsOfService(classification = "C"))
68+
assertEquals(defaultScore, privacyMonitor.score)
69+
}
70+
71+
@Test
72+
fun whenTermsClassificationIsDThenScoreIsIncrementedByOne() {
73+
val privacyMonitor = monitor(terms = TermsOfService(classification = "D"))
74+
assertEquals(defaultScore + 1, privacyMonitor.score)
75+
}
76+
77+
@Test
78+
fun whenTermsClassificationIsEThenScoreIsIncrementedByTwo() {
79+
val privacyMonitor = monitor(terms = TermsOfService(classification = "E"))
80+
assertEquals(defaultScore + 2, privacyMonitor.score)
81+
}
82+
83+
@Test
84+
fun whenTermsScoreIsPositiveThenScoreIsIncrementedByOne() {
85+
val privacyMonitor = monitor(terms = TermsOfService(score = 5))
86+
assertEquals(defaultScore + 1, privacyMonitor.score)
87+
}
88+
89+
@Test
90+
fun whenTermsScoreIsNegativeThenScoreIsDecrementedByOne() {
91+
val privacyMonitor = monitor(terms = TermsOfService(score = -5))
92+
assertEquals(defaultScore - 1, privacyMonitor.score)
93+
}
94+
95+
@Test
96+
fun whenTermsScoreIsZeroThenScoreIsUnchanged() {
97+
val privacyMonitor = monitor(terms = TermsOfService(score = 0))
98+
assertEquals(defaultScore, privacyMonitor.score)
99+
}
100+
101+
@Test
102+
fun whenZeroTrackersThenScoreIsDefault() {
103+
val privacyMonitor = monitor(trackerCount = 0)
104+
assertEquals(defaultScore, privacyMonitor.score)
105+
}
106+
107+
@Test
108+
fun whenOneTrackerThenScoreIsIncrementedByOne() {
109+
val privacyMonitor = monitor(trackerCount = 1)
110+
assertEquals(defaultScore + 1, privacyMonitor.score)
111+
}
112+
113+
@Test
114+
fun whenElevenTrackersThenScoreIsIncrementedByTwo() {
115+
val privacyMonitor = monitor(trackerCount = 11)
116+
assertEquals(defaultScore + 2, privacyMonitor.score)
117+
}
118+
119+
@Test
120+
fun whenSiteIsMajorNetworkMemberThenScoreIsIncrementedByPercentage() {
121+
val privacyMonitor = monitor(memberNetwork = TrackerNetwork("", "", 45, true))
122+
assertEquals(defaultScore + 5, privacyMonitor.score)
123+
}
124+
125+
@Test
126+
fun whenHasMajorTrackerThenScoreIsIncrementedByOne() {
127+
val privacyMonitor = monitor(majorTrackerCount = 1)
128+
assertEquals(defaultScore + 1, privacyMonitor.score)
129+
}
130+
131+
@Test
132+
fun whenHasObscureTrackerThenScoreIsIncrementedByOne() {
133+
val privacyMonitor = monitor(hasObscureTracker = true)
134+
assertEquals(defaultScore + 1, privacyMonitor.score)
135+
}
136+
137+
138+
@Test
139+
fun whenImprovedScoreThenTrackerMetricsIgnored() {
140+
val privacyMonitor = monitor(
141+
TrackerNetwork("", "", 5, true),
142+
TermsOfService(classification = "D"),
143+
NONE,
144+
5,
145+
2,
146+
true)
147+
assertEquals(defaultScore + 6, privacyMonitor.score)
148+
assertEquals(defaultScore + 3, privacyMonitor.improvedScore)
149+
}
150+
151+
private fun monitor(memberNetwork: TrackerNetwork? = null,
152+
terms: TermsOfService = TermsOfService(),
153+
https: HttpsStatus = SECURE,
154+
trackerCount: Int = 0,
155+
majorTrackerCount: Int = 0,
156+
hasObscureTracker: Boolean = false): PrivacyMonitor {
157+
158+
val monitor: PrivacyMonitor = mock()
159+
whenever(monitor.memberNetwork).thenReturn(memberNetwork)
160+
whenever(monitor.termsOfService).thenReturn(terms)
161+
whenever(monitor.trackerCount).thenReturn(trackerCount)
162+
whenever(monitor.majorNetworkCount).thenReturn(majorTrackerCount)
163+
whenever(monitor.hasObscureTracker).thenReturn(hasObscureTracker)
164+
whenever(monitor.https).thenReturn(https)
165+
return monitor
166+
}
167+
}

app/src/androidTest/java/com/duckduckgo/app/privacymonitor/SiteMonitorInstrumentationTests.kt

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,39 +16,51 @@
1616

1717
package com.duckduckgo.app.privacymonitor
1818

19+
import com.duckduckgo.app.privacymonitor.model.TermsOfService
20+
import com.duckduckgo.app.trackerdetection.model.TrackerNetworks
21+
import com.duckduckgo.app.trackerdetection.model.TrackingEvent
1922
import org.junit.Assert.assertEquals
23+
import org.junit.Assert.assertTrue
2024
import org.junit.Test
2125

2226
class SiteMonitorInstrumentationTests {
2327

2428
companion object {
2529
private const val httpDocument = "http://example.com"
2630
private const val httpsDocument = "https://example.com"
27-
private const val malformesDocument = "[example com]"
31+
private const val malformedDocument = "[example com]"
2832
}
2933

3034
@Test
3135
fun whenUrlIsHttpsThenHttpsStatusIsSecure() {
32-
val testee = SiteMonitor(httpsDocument)
36+
val testee = SiteMonitor(httpsDocument, TermsOfService(), TrackerNetworks())
3337
assertEquals(HttpsStatus.SECURE, testee.https)
3438
}
3539

3640
@Test
3741
fun whenUrlIsHttpThenHttpsStatusIsNone() {
38-
val testee = SiteMonitor(httpDocument)
42+
val testee = SiteMonitor(httpDocument, TermsOfService(), TrackerNetworks())
3943
assertEquals(HttpsStatus.NONE, testee.https)
4044
}
4145

4246
@Test
4347
fun whenUrlIsHttpsWithHttpResourcesThenHttpsStatusIsMixed() {
44-
val testee = SiteMonitor(httpsDocument)
48+
val testee = SiteMonitor(httpsDocument, TermsOfService(), TrackerNetworks())
4549
testee.hasHttpResources = true
4650
assertEquals(HttpsStatus.MIXED, testee.https)
4751
}
4852

4953
@Test
5054
fun whenUrlIsMalformedThenHttpsStatusIsNone() {
51-
val testee = SiteMonitor(malformesDocument)
55+
val testee = SiteMonitor(malformedDocument, TermsOfService(), TrackerNetworks())
5256
assertEquals(HttpsStatus.NONE, testee.https)
5357
}
58+
59+
@Test
60+
fun whenIpTrackerDetectedThenHasObscureTrackerIsTrue() {
61+
val testee = SiteMonitor(httpDocument, TermsOfService(), TrackerNetworks())
62+
testee.trackerDetected(TrackingEvent(httpDocument, "http://54.229.105.203/abc", null, true))
63+
assertTrue(testee.hasObscureTracker)
64+
}
65+
5466
}

app/src/androidTest/java/com/duckduckgo/app/privacymonitor/ui/PrivacyDashboardViewModelTest.kt

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,60 @@ class PrivacyDashboardViewModelTest {
8787
assertFalse(testee.shouldReloadPage)
8888
}
8989

90+
@Test
91+
fun whenFullUpgradeThenHeadingIndicatesUpgrade() {
92+
val monitor = monitor()
93+
whenever(monitor.allTrackersBlocked).thenReturn(true)
94+
whenever(monitor.majorNetworkCount).thenReturn(2)
95+
testee.onPrivacyMonitorChanged(monitor)
96+
assertTrue(testee.viewState.value!!.heading.contains("UPGRADED FROM"))
97+
}
98+
99+
@Test
100+
fun whenPartialUpgradeAndPrivacyOnThenHeadingIndicatesEnabled() {
101+
whenever(settingStore.privacyOn).thenReturn(true)
102+
val monitor = monitor()
103+
whenever(monitor.allTrackersBlocked).thenReturn(false)
104+
whenever(monitor.majorNetworkCount).thenReturn(2)
105+
testee.onPrivacyMonitorChanged(monitor)
106+
assertEquals(getStringResource(R.string.privacyProtectionEnabled), testee.viewState.value?.heading)
107+
}
108+
109+
@Test
110+
fun whenPartialUpgradeAndPrivacyOffThenHeadingIndicatesDisabled() {
111+
whenever(settingStore.privacyOn).thenReturn(false)
112+
val monitor = monitor()
113+
whenever(monitor.allTrackersBlocked).thenReturn(false)
114+
whenever(monitor.majorNetworkCount).thenReturn(2)
115+
testee.onPrivacyMonitorChanged(monitor)
116+
assertEquals(getStringResource(R.string.privacyProtectionDisabled), testee.viewState.value?.heading)
117+
}
118+
119+
@Test
120+
fun whenNoUpgradeAndPrivacyOnThenHeadingIndicatesEnabled() {
121+
whenever(settingStore.privacyOn).thenReturn(true)
122+
val monitor = monitor()
123+
whenever(monitor.allTrackersBlocked).thenReturn(true)
124+
testee.onPrivacyMonitorChanged(monitor)
125+
assertEquals(getStringResource(R.string.privacyProtectionEnabled), testee.viewState.value?.heading)
126+
}
127+
128+
@Test
129+
fun whenNoUpgradeAndPrivacyOffThenHeadingIndicatesDisabled() {
130+
whenever(settingStore.privacyOn).thenReturn(false)
131+
val monitor = monitor()
132+
whenever(monitor.allTrackersBlocked).thenReturn(false)
133+
whenever(monitor.majorNetworkCount).thenReturn(2)
134+
testee.onPrivacyMonitorChanged(monitor)
135+
assertEquals(getStringResource(R.string.privacyProtectionDisabled), testee.viewState.value?.heading)
136+
}
137+
138+
@Test
139+
fun whenPrivacyOnAndNoUpgradeThenHeadingIndicatesEnabled() {
140+
whenever(settingStore.privacyOn).thenReturn(true)
141+
assertEquals(getStringResource(R.string.privacyProtectionEnabled), testee.viewState.value?.heading)
142+
}
143+
90144
@Test
91145
fun whenHttpsStatusIsSecureThenTextAndIconReflectSame() {
92146
val monitor = monitor()

app/src/androidTest/java/com/duckduckgo/app/trackerdetection/model/ResourceTypeInstrumentationTest.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -87,9 +87,9 @@ class ResourceTypeInstrumentationTest {
8787
assertEquals(UNKNOWN, ResourceType.from(requestMock))
8888
}
8989

90-
private fun buildRequestMock(accpetHeader: String, url: Uri): WebResourceRequest {
90+
private fun buildRequestMock(acceptHeader: String, url: Uri): WebResourceRequest {
9191
val requestMock: WebResourceRequest = mock()
92-
val headers: HashMap<String, String> = hashMapOf("Accept" to accpetHeader)
92+
val headers: HashMap<String, String> = hashMapOf("Accept" to acceptHeader)
9393
whenever(requestMock.url).thenReturn(url)
9494
whenever(requestMock.requestHeaders).thenReturn(headers)
9595
return requestMock

app/src/androidTest/java/com/duckduckgo/app/trackerdetection/model/TrackerNetworksInstrumentationTest.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ class TrackerNetworksInstrumentationTest {
8181
}
8282

8383
@Test
84-
fun whenUrlContainsButIsNotSubdomainOfNetworkrUrlThenNullIsReturned() {
84+
fun whenUrlContainsButIsNotSubdomainOfNetworkUrlThenNullIsReturned() {
8585
val data = listOf(DisconnectTracker("tracker.com", category, networkName, networkUrl))
8686
testee.updateData(data)
8787
assertNull(testee.network("http://notsubdomainofnetwork.com/index.html"))

app/src/main/java/com/duckduckgo/app/browser/BrowserViewModel.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,15 @@ import com.duckduckgo.app.privacymonitor.SiteMonitor
2424
import com.duckduckgo.app.privacymonitor.model.TermsOfService
2525
import com.duckduckgo.app.privacymonitor.store.PrivacyMonitorRepository
2626
import com.duckduckgo.app.privacymonitor.store.TermsOfServiceStore
27+
import com.duckduckgo.app.trackerdetection.model.TrackerNetworks
2728
import com.duckduckgo.app.trackerdetection.model.TrackingEvent
2829
import timber.log.Timber
2930

3031
class BrowserViewModel(
3132
private val queryUrlConverter: OmnibarEntryConverter,
3233
private val duckDuckGoUrlDetector: DuckDuckGoUrlDetector,
3334
private val termsOfServiceStore: TermsOfServiceStore,
35+
private val trackerNetworks: TrackerNetworks,
3436
private val privacyMonitorRepository: PrivacyMonitorRepository) :
3537
WebViewClientListener, ViewModel() {
3638

@@ -110,7 +112,7 @@ class BrowserViewModel(
110112
viewState.value = newViewState
111113
if (url != null) {
112114
val terms = termsOfServiceStore.retrieveTerms(url) ?: TermsOfService()
113-
siteMonitor = SiteMonitor(url, terms)
115+
siteMonitor = SiteMonitor(url, terms, trackerNetworks)
114116
postSiteMonitor()
115117
}
116118
}

0 commit comments

Comments
 (0)